pmc_core_xtal_ignore(pmc);
 }
 
+static void pmc_core_clean_structure(struct platform_device *pdev)
+{
+       struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
+               struct pmc *pmc = pmcdev->pmcs[i];
+
+               if (pmc)
+                       iounmap(pmc->regbase);
+       }
+
+       if (pmcdev->ssram_pcidev) {
+               pci_dev_put(pmcdev->ssram_pcidev);
+               pci_disable_device(pmcdev->ssram_pcidev);
+       }
+       platform_set_drvdata(pdev, NULL);
+       mutex_destroy(&pmcdev->lock);
+}
+
 static int pmc_core_probe(struct platform_device *pdev)
 {
        static bool device_initialized;
        mutex_init(&pmcdev->lock);
        ret = core_init(pmcdev);
        if (ret) {
-               mutex_destroy(&pmcdev->lock);
+               pmc_core_clean_structure(pdev);
                return ret;
        }
 
 static void pmc_core_remove(struct platform_device *pdev)
 {
        struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
-               struct pmc *pmc = pmcdev->pmcs[i];
-
-               if (pmc)
-                       iounmap(pmc->regbase);
-       }
-
        pmc_core_dbgfs_unregister(pmcdev);
-       platform_set_drvdata(pdev, NULL);
-       mutex_destroy(&pmcdev->lock);
+       pmc_core_clean_structure(pdev);
 }
 
 static bool warn_on_s0ix_failures;
 
        const u32 etr3_offset;
 };
 
+/**
+ * struct pmc_info - Structure to keep pmc info
+ * @devid:             device id of the pmc device
+ * @map:               pointer to a pmc_reg_map struct that contains platform
+ *                     specific attributes
+ */
+struct pmc_info {
+       u16 devid;
+       const struct pmc_reg_map *map;
+};
+
 /**
  * struct pmc - pmc private info structure
  * @base_addr:         contains pmc base address
  * struct pmc_dev - pmc device structure
  * @devs:              pointer to an array of pmc pointers
  * @pdev:              pointer to platform_device struct
+ * @ssram_pcidev:      pointer to pci device struct for the PMC SSRAM
  * @dbgfs_dir:         path to debugfs interface
  * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
  *                     used to read MPHY PG and PLL status are available
        struct pmc *pmcs[MAX_NUM_PMC];
        struct dentry *dbgfs_dir;
        struct platform_device *pdev;
+       struct pci_dev *ssram_pcidev;
        int pmc_xram_read_bit;
        struct mutex lock; /* generic mutex lock for PMC Core */
 
        bool has_die_c6;
        u32 die_c6_offset;
        struct telem_endpoint *punit_ep;
+       struct pmc_info *regmap_list;
 };
 
 enum pmc_index {
 int pmc_core_resume_common(struct pmc_dev *pmcdev);
 int get_primary_reg_base(struct pmc *pmc);
 
+extern void pmc_core_ssram_init(struct pmc_dev *pmcdev);
+
 int spt_core_init(struct pmc_dev *pmcdev);
 int cnp_core_init(struct pmc_dev *pmcdev);
 int icl_core_init(struct pmc_dev *pmcdev);
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains functions to handle discovery of PMC metrics located
+ * in the PMC SSRAM PCI device.
+ *
+ * Copyright (c) 2023, Intel Corporation.
+ * All Rights Reserved.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+#include "core.h"
+
+#define SSRAM_HDR_SIZE         0x100
+#define SSRAM_PWRM_OFFSET      0x14
+#define SSRAM_DVSEC_OFFSET     0x1C
+#define SSRAM_DVSEC_SIZE       0x10
+#define SSRAM_PCH_OFFSET       0x60
+#define SSRAM_IOE_OFFSET       0x68
+#define SSRAM_DEVID_OFFSET     0x70
+
+static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid)
+{
+       for (; list->map; ++list)
+               if (devid == list->devid)
+                       return list->map;
+
+       return NULL;
+}
+
+static inline u64 get_base(void __iomem *addr, u32 offset)
+{
+       return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3);
+}
+
+static void
+pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base,
+                const struct pmc_reg_map *reg_map, int pmc_index)
+{
+       struct pmc *pmc = pmcdev->pmcs[pmc_index];
+
+       if (!pwrm_base)
+               return;
+
+       /* Memory for primary PMC has been allocated in core.c */
+       if (!pmc) {
+               pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL);
+               if (!pmc)
+                       return;
+       }
+
+       pmc->map = reg_map;
+       pmc->base_addr = pwrm_base;
+       pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length);
+
+       if (!pmc->regbase) {
+               devm_kfree(&pmcdev->pdev->dev, pmc);
+               return;
+       }
+
+       pmcdev->pmcs[pmc_index] = pmc;
+}
+
+static void
+pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset,
+                      int pmc_idx)
+{
+       u64 pwrm_base;
+       u16 devid;
+
+       if (pmc_idx != PMC_IDX_SOC) {
+               u64 ssram_base = get_base(ssram, offset);
+
+               if (!ssram_base)
+                       return;
+
+               ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
+               if (!ssram)
+                       return;
+       }
+
+       pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
+       devid = readw(ssram + SSRAM_DEVID_OFFSET);
+
+       if (pmcdev->regmap_list) {
+               const struct pmc_reg_map *map;
+
+               map = pmc_core_find_regmap(pmcdev->regmap_list, devid);
+               if (map)
+                       pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx);
+       }
+
+       if (pmc_idx != PMC_IDX_SOC)
+               iounmap(ssram);
+}
+
+void pmc_core_ssram_init(struct pmc_dev *pmcdev)
+{
+       void __iomem *ssram;
+       struct pci_dev *pcidev;
+       u64 ssram_base;
+       int ret;
+
+       pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2));
+       if (!pcidev)
+               goto out;
+
+       ret = pcim_enable_device(pcidev);
+       if (ret)
+               goto release_dev;
+
+       ssram_base = pcidev->resource[0].start;
+       ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
+       if (!ssram)
+               goto disable_dev;
+
+       pmcdev->ssram_pcidev = pcidev;
+
+       pmc_core_ssram_get_pmc(pmcdev, ssram, 0, PMC_IDX_SOC);
+       pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_IOE_OFFSET, PMC_IDX_IOE);
+       pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH);
+
+       iounmap(ssram);
+out:
+       return;
+
+disable_dev:
+       pci_disable_device(pcidev);
+release_dev:
+       pci_dev_put(pcidev);
+}