All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v5 0/8] PMC/PUNIT IPC driver cleanup
@ 2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Hi All,

Currently intel_pmc_ipc.c, intel_punit_ipc.c, intel_scu_ipc.c drivers implements the same IPC features.
This code duplication could be avoided if we implement the IPC driver as a generic library and let custom
device drivers use API provided by generic driver. This patchset mainly addresses this issue.

Along with above code duplication issue, This patchset also addresses following issues in intel_pmc_ipc and
intel_punit_ipc drivers. 

1. Intel_pmc_ipc.c driver does not use any resource managed (devm_*) calls.
2. In Intel_pmc_ipc.c driver, dependent devices like PUNIT, Telemetry and iTCO are created manually and uses lot of redundant buffer code.
3. Global variable is used to store the IPC device structure and it is used across all functions in intel_pmc_ipc.c and intel_punit_ipc.c.

More info on Intel IPC device library:
-------------------------------------

A generic Intel IPC class driver has been implemented and all common IPC helper functions has been moved to this driver. It exposes APIs to create IPC device channel, send raw IPC command and simple IPC commands. It also creates device attribute to send IPC command from user space.

API for creating a new IPC channel device is,

struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev, const char *devname, struct intel_ipc_dev_cfg *cfg, struct intel_ipc_dev_ops *ops)

The IPC channel drivers (PUNIT/PMC/SCU) when creating a new device can configure their device params like register mapping, irq, irq-mode, channel type,etc  using intel_ipc_dev_cfg and intel_ipc_dev_ops arguments. After a new IPC channel device is created, IPC users can use the generic APIs to make IPC calls.

For example, after using this new model, IPC call to PMC device will look like,

pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, (u32 *)ipc_in, 1, NULL, 0, 0, 0);
intel_ipc_dev_put(pmc_ipc_dev);

I am still testing the driver in different products. But posted it to get some early comments. I also welcome any PMC/PUNIT driver users to check these patches in their product.

Changes since v4:
 * Pushed "platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR update" patch
   to the top of the patch list. Also its rebased on top of Andy's review branch.

Changes since v3:
 * Rebased on top of Andy's review branch.
 * Fixed resource ioremap warning in intel_punit_ipc.c.
 * Divided "platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates" patch
   into two patches. One fixing the existing issue and another to add regmap support.
 * Fixed error in resource initalization logic in ipc_create_punit_device.
 * Changed PLATFORM_DEVID_AUTO to PLATFORM_DEVID_NONE in mfd device creation.
 * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * In intel_ipc_dev.c,
    * Fixed error in check for duplicate intel_ipc_dev.
    * Added custom interrupt handler support.
    * Used char array for error string conversion.
    * Added put dev support.

Changes since v2:
 * Refactored intel_scu_ipc.c to use generic IPC device APIs.
 * Fixed intel_pmc_ipc.c to use pcim_* device managed functions.

Changes since v1:
 * Merged devm_* changes in pmc_plat_probe and pmc_pci_probe functions into a
   single patch.
 * Addressed Andy's comment about keeping the library generic by not implementing
   the low level reg access calls in intel_ipc_dev.c. This version will start using
   the regmap pointer provided by channel drivers instead of fixed memory map.
 * Removed custom IPC APIs in intel_pmc_ipc.c and intel_punit_ipc.c.
 * Cleaned up IPC driver users to use APIs provided by generic library (intel_ipc_dev.c).



Kuppuswamy Sathyanarayanan (8):
  platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  platform/x86: intel_pmc_ipc: Use MFD framework to create dependent
    devices
  platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
  platform: x86: Add generic Intel IPC driver
  platform/x86: intel_punit_ipc: Fix resource ioremap warning
  platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
  platform/x86: intel_scu_ipc: Use generic Intel IPC device calls

 arch/x86/include/asm/intel_pmc_ipc.h            |  37 +-
 arch/x86/include/asm/intel_punit_ipc.h          | 125 ++--
 arch/x86/include/asm/intel_scu_ipc.h            |  23 +-
 arch/x86/platform/intel-mid/intel-mid.c         |  15 +-
 drivers/mfd/intel_soc_pmic_bxtwc.c              |  21 +-
 drivers/platform/x86/Kconfig                    |  11 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 +++++++++++++++
 drivers/platform/x86/intel_pmc_ipc.c            | 886 +++++++++---------------
 drivers/platform/x86/intel_punit_ipc.c          | 311 +++------
 drivers/platform/x86/intel_scu_ipc.c            | 483 ++++++-------
 drivers/platform/x86/intel_telemetry_pltdrv.c   | 212 +++---
 drivers/rtc/rtc-mrst.c                          |  16 +-
 drivers/watchdog/intel-mid_wdt.c                |  12 +-
 drivers/watchdog/intel_scu_watchdog.c           |  18 +-
 include/linux/mfd/intel_soc_pmic.h              |   2 +
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 ++++++
 17 files changed, 1683 insertions(+), 1272 deletions(-)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [RFC v5 0/8] PMC/PUNIT IPC driver cleanup
@ 2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Hi All,

Currently intel_pmc_ipc.c, intel_punit_ipc.c, intel_scu_ipc.c drivers implements the same IPC features.
This code duplication could be avoided if we implement the IPC driver as a generic library and let custom
device drivers use API provided by generic driver. This patchset mainly addresses this issue.

Along with above code duplication issue, This patchset also addresses following issues in intel_pmc_ipc and
intel_punit_ipc drivers. 

1. Intel_pmc_ipc.c driver does not use any resource managed (devm_*) calls.
2. In Intel_pmc_ipc.c driver, dependent devices like PUNIT, Telemetry and iTCO are created manually and uses lot of redundant buffer code.
3. Global variable is used to store the IPC device structure and it is used across all functions in intel_pmc_ipc.c and intel_punit_ipc.c.

More info on Intel IPC device library:
-------------------------------------

A generic Intel IPC class driver has been implemented and all common IPC helper functions has been moved to this driver. It exposes APIs to create IPC device channel, send raw IPC command and simple IPC commands. It also creates device attribute to send IPC command from user space.

API for creating a new IPC channel device is,

struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev, const char *devname, struct intel_ipc_dev_cfg *cfg, struct intel_ipc_dev_ops *ops)

The IPC channel drivers (PUNIT/PMC/SCU) when creating a new device can configure their device params like register mapping, irq, irq-mode, channel type,etc  using intel_ipc_dev_cfg and intel_ipc_dev_ops arguments. After a new IPC channel device is created, IPC users can use the generic APIs to make IPC calls.

For example, after using this new model, IPC call to PMC device will look like,

pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, (u32 *)ipc_in, 1, NULL, 0, 0, 0);
intel_ipc_dev_put(pmc_ipc_dev);

I am still testing the driver in different products. But posted it to get some early comments. I also welcome any PMC/PUNIT driver users to check these patches in their product.

Changes since v4:
 * Pushed "platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR update" patch
   to the top of the patch list. Also its rebased on top of Andy's review branch.

Changes since v3:
 * Rebased on top of Andy's review branch.
 * Fixed resource ioremap warning in intel_punit_ipc.c.
 * Divided "platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates" patch
   into two patches. One fixing the existing issue and another to add regmap support.
 * Fixed error in resource initalization logic in ipc_create_punit_device.
 * Changed PLATFORM_DEVID_AUTO to PLATFORM_DEVID_NONE in mfd device creation.
 * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * In intel_ipc_dev.c,
    * Fixed error in check for duplicate intel_ipc_dev.
    * Added custom interrupt handler support.
    * Used char array for error string conversion.
    * Added put dev support.

Changes since v2:
 * Refactored intel_scu_ipc.c to use generic IPC device APIs.
 * Fixed intel_pmc_ipc.c to use pcim_* device managed functions.

Changes since v1:
 * Merged devm_* changes in pmc_plat_probe and pmc_pci_probe functions into a
   single patch.
 * Addressed Andy's comment about keeping the library generic by not implementing
   the low level reg access calls in intel_ipc_dev.c. This version will start using
   the regmap pointer provided by channel drivers instead of fixed memory map.
 * Removed custom IPC APIs in intel_pmc_ipc.c and intel_punit_ipc.c.
 * Cleaned up IPC driver users to use APIs provided by generic library (intel_ipc_dev.c).



Kuppuswamy Sathyanarayanan (8):
  platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  platform/x86: intel_pmc_ipc: Use MFD framework to create dependent
    devices
  platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
  platform: x86: Add generic Intel IPC driver
  platform/x86: intel_punit_ipc: Fix resource ioremap warning
  platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
  platform/x86: intel_scu_ipc: Use generic Intel IPC device calls

 arch/x86/include/asm/intel_pmc_ipc.h            |  37 +-
 arch/x86/include/asm/intel_punit_ipc.h          | 125 ++--
 arch/x86/include/asm/intel_scu_ipc.h            |  23 +-
 arch/x86/platform/intel-mid/intel-mid.c         |  15 +-
 drivers/mfd/intel_soc_pmic_bxtwc.c              |  21 +-
 drivers/platform/x86/Kconfig                    |  11 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 +++++++++++++++
 drivers/platform/x86/intel_pmc_ipc.c            | 886 +++++++++---------------
 drivers/platform/x86/intel_punit_ipc.c          | 311 +++------
 drivers/platform/x86/intel_scu_ipc.c            | 483 ++++++-------
 drivers/platform/x86/intel_telemetry_pltdrv.c   | 212 +++---
 drivers/rtc/rtc-mrst.c                          |  16 +-
 drivers/watchdog/intel-mid_wdt.c                |  12 +-
 drivers/watchdog/intel_scu_watchdog.c           |  18 +-
 include/linux/mfd/intel_soc_pmic.h              |   2 +
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 ++++++
 17 files changed, 1683 insertions(+), 1272 deletions(-)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [RFC v5 0/8] PMC/PUNIT IPC driver cleanup
@ 2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, x86-DgEjT+Ai2ygdnm+yROfE0A,
	wim-IQzOog9fTRqzQB+pC5nmwQ, mingo-H+wXaHxf7aLQT0dZR+AlfA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	qipeng.zha-ral2JQCrhuEAvxtiuMwx3w, hpa-YMNOUZJC4hwAvxtiuMwx3w,
	dvhart-wEGCiKHe2LqWVfeAwA7xHQ, tglx-hfZtesqFncYOwBW4kG4KsQ,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A, andy-wEGCiKHe2LqWVfeAwA7xHQ,
	souvik.k.chakravarty-ral2JQCrhuEAvxtiuMwx3w
  Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	platform-driver-x86-u79uwXL29TY76Z2rM5mHXA,
	sathyaosid-Re5JQEeQqe8AvxtiuMwx3w, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

Hi All,

Currently intel_pmc_ipc.c, intel_punit_ipc.c, intel_scu_ipc.c drivers implements the same IPC features.
This code duplication could be avoided if we implement the IPC driver as a generic library and let custom
device drivers use API provided by generic driver. This patchset mainly addresses this issue.

Along with above code duplication issue, This patchset also addresses following issues in intel_pmc_ipc and
intel_punit_ipc drivers. 

1. Intel_pmc_ipc.c driver does not use any resource managed (devm_*) calls.
2. In Intel_pmc_ipc.c driver, dependent devices like PUNIT, Telemetry and iTCO are created manually and uses lot of redundant buffer code.
3. Global variable is used to store the IPC device structure and it is used across all functions in intel_pmc_ipc.c and intel_punit_ipc.c.

More info on Intel IPC device library:
-------------------------------------

A generic Intel IPC class driver has been implemented and all common IPC helper functions has been moved to this driver. It exposes APIs to create IPC device channel, send raw IPC command and simple IPC commands. It also creates device attribute to send IPC command from user space.

API for creating a new IPC channel device is,

struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev, const char *devname, struct intel_ipc_dev_cfg *cfg, struct intel_ipc_dev_ops *ops)

The IPC channel drivers (PUNIT/PMC/SCU) when creating a new device can configure their device params like register mapping, irq, irq-mode, channel type,etc  using intel_ipc_dev_cfg and intel_ipc_dev_ops arguments. After a new IPC channel device is created, IPC users can use the generic APIs to make IPC calls.

For example, after using this new model, IPC call to PMC device will look like,

pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, (u32 *)ipc_in, 1, NULL, 0, 0, 0);
intel_ipc_dev_put(pmc_ipc_dev);

I am still testing the driver in different products. But posted it to get some early comments. I also welcome any PMC/PUNIT driver users to check these patches in their product.

Changes since v4:
 * Pushed "platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR update" patch
   to the top of the patch list. Also its rebased on top of Andy's review branch.

Changes since v3:
 * Rebased on top of Andy's review branch.
 * Fixed resource ioremap warning in intel_punit_ipc.c.
 * Divided "platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates" patch
   into two patches. One fixing the existing issue and another to add regmap support.
 * Fixed error in resource initalization logic in ipc_create_punit_device.
 * Changed PLATFORM_DEVID_AUTO to PLATFORM_DEVID_NONE in mfd device creation.
 * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * In intel_ipc_dev.c,
    * Fixed error in check for duplicate intel_ipc_dev.
    * Added custom interrupt handler support.
    * Used char array for error string conversion.
    * Added put dev support.

Changes since v2:
 * Refactored intel_scu_ipc.c to use generic IPC device APIs.
 * Fixed intel_pmc_ipc.c to use pcim_* device managed functions.

Changes since v1:
 * Merged devm_* changes in pmc_plat_probe and pmc_pci_probe functions into a
   single patch.
 * Addressed Andy's comment about keeping the library generic by not implementing
   the low level reg access calls in intel_ipc_dev.c. This version will start using
   the regmap pointer provided by channel drivers instead of fixed memory map.
 * Removed custom IPC APIs in intel_pmc_ipc.c and intel_punit_ipc.c.
 * Cleaned up IPC driver users to use APIs provided by generic library (intel_ipc_dev.c).



Kuppuswamy Sathyanarayanan (8):
  platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  platform/x86: intel_pmc_ipc: Use MFD framework to create dependent
    devices
  platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
  platform: x86: Add generic Intel IPC driver
  platform/x86: intel_punit_ipc: Fix resource ioremap warning
  platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
  platform/x86: intel_scu_ipc: Use generic Intel IPC device calls

 arch/x86/include/asm/intel_pmc_ipc.h            |  37 +-
 arch/x86/include/asm/intel_punit_ipc.h          | 125 ++--
 arch/x86/include/asm/intel_scu_ipc.h            |  23 +-
 arch/x86/platform/intel-mid/intel-mid.c         |  15 +-
 drivers/mfd/intel_soc_pmic_bxtwc.c              |  21 +-
 drivers/platform/x86/Kconfig                    |  11 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 +++++++++++++++
 drivers/platform/x86/intel_pmc_ipc.c            | 886 +++++++++---------------
 drivers/platform/x86/intel_punit_ipc.c          | 311 +++------
 drivers/platform/x86/intel_scu_ipc.c            | 483 ++++++-------
 drivers/platform/x86/intel_telemetry_pltdrv.c   | 212 +++---
 drivers/rtc/rtc-mrst.c                          |  16 +-
 drivers/watchdog/intel-mid_wdt.c                |  12 +-
 drivers/watchdog/intel_scu_watchdog.c           |  18 +-
 include/linux/mfd/intel_soc_pmic.h              |   2 +
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 ++++++
 17 files changed, 1683 insertions(+), 1272 deletions(-)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan,
	Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently, update_no_reboot_bit() function implemented in this driver
uses mutex_lock() to protect its register updates. But this function is
called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
functions in iTCO_wdt.c driver, which in turn causes "sleeping into
atomic context" issue. This patch fixes this issue by replacing the
mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.

Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_pmc_ipc.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

Changes since v4:
 * Fixed style issues in commit header.
 * Rebased this patch on top of Andy's review branch.

diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 751b121..e03fa314 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -33,6 +33,7 @@
 #include <linux/suspend.h>
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/spinlock.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -131,6 +132,7 @@ static struct intel_pmc_ipc_dev {
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
+	spinlock_t gcr_lock;
 
 	/* punit */
 	struct platform_device *punit_dev;
@@ -225,17 +227,17 @@ int intel_pmc_gcr_read(u32 offset, u32 *data)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0) {
-		mutex_unlock(&ipclock);
+		spin_unlock(&ipcdev.gcr_lock);
 		return ret;
 	}
 
 	*data = readl(ipcdev.gcr_mem_base + offset);
 
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 
 	return 0;
 }
@@ -255,17 +257,17 @@ int intel_pmc_gcr_write(u32 offset, u32 data)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0) {
-		mutex_unlock(&ipclock);
+		spin_unlock(&ipcdev.gcr_lock);
 		return ret;
 	}
 
 	writel(data, ipcdev.gcr_mem_base + offset);
 
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 
 	return 0;
 }
@@ -287,7 +289,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 	u32 new_val;
 	int ret = 0;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0)
@@ -309,7 +311,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 	}
 
 gcr_ipc_unlock:
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
@@ -489,6 +491,8 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
 
+	spin_lock_init(&ipcdev.gcr_lock);
+
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -903,6 +907,7 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	ipcdev.dev = &pdev->dev;
 	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
 	init_completion(&ipcdev.cmd_complete);
+	spin_lock_init(&ipcdev.gcr_lock);
 
 	ipcdev.irq = platform_get_irq(pdev, 0);
 	if (ipcdev.irq < 0) {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan,
	Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently, update_no_reboot_bit() function implemented in this driver
uses mutex_lock() to protect its register updates. But this function is
called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
functions in iTCO_wdt.c driver, which in turn causes "sleeping into
atomic context" issue. This patch fixes this issue by replacing the
mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.

Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_pmc_ipc.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

Changes since v4:
 * Fixed style issues in commit header.
 * Rebased this patch on top of Andy's review branch.

diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 751b121..e03fa314 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -33,6 +33,7 @@
 #include <linux/suspend.h>
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/spinlock.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -131,6 +132,7 @@ static struct intel_pmc_ipc_dev {
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
+	spinlock_t gcr_lock;
 
 	/* punit */
 	struct platform_device *punit_dev;
@@ -225,17 +227,17 @@ int intel_pmc_gcr_read(u32 offset, u32 *data)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0) {
-		mutex_unlock(&ipclock);
+		spin_unlock(&ipcdev.gcr_lock);
 		return ret;
 	}
 
 	*data = readl(ipcdev.gcr_mem_base + offset);
 
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 
 	return 0;
 }
@@ -255,17 +257,17 @@ int intel_pmc_gcr_write(u32 offset, u32 data)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0) {
-		mutex_unlock(&ipclock);
+		spin_unlock(&ipcdev.gcr_lock);
 		return ret;
 	}
 
 	writel(data, ipcdev.gcr_mem_base + offset);
 
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 
 	return 0;
 }
@@ -287,7 +289,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 	u32 new_val;
 	int ret = 0;
 
-	mutex_lock(&ipclock);
+	spin_lock(&ipcdev.gcr_lock);
 
 	ret = is_gcr_valid(offset);
 	if (ret < 0)
@@ -309,7 +311,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 	}
 
 gcr_ipc_unlock:
-	mutex_unlock(&ipclock);
+	spin_unlock(&ipcdev.gcr_lock);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
@@ -489,6 +491,8 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
 
+	spin_lock_init(&ipcdev.gcr_lock);
+
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -903,6 +907,7 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	ipcdev.dev = &pdev->dev;
 	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
 	init_completion(&ipcdev.cmd_complete);
+	spin_lock_init(&ipcdev.gcr_lock);
 
 	ipcdev.irq = platform_get_irq(pdev, 0);
 	if (ipcdev.irq < 0) {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 2/8] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently, we have lot of repetitive code in dependent device resource
allocation and device creation handling code. This logic can be improved if
we use MFD framework for dependent device creation. This patch adds this
support.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_pmc_ipc.c | 394 ++++++++++++-----------------------
 1 file changed, 137 insertions(+), 257 deletions(-)

Changes since v4:
 * Changed the order of patches in this patchlist.

Changes since v3:
 * Changed PLATFORM_DEVID_AUTO to PLATFORM_DEVID_NONE in mfd device creation.
 * Fixed error in resource initalization logic in ipc_create_punit_device.
 * Removed mfd cell id initialization.

diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index e03fa314..18e94fa 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -34,6 +34,7 @@
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/spinlock.h>
+#include <linux/mfd/core.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -88,6 +89,7 @@
 #define PLAT_RESOURCE_ISP_IFACE_INDEX	5
 #define PLAT_RESOURCE_GTD_DATA_INDEX	6
 #define PLAT_RESOURCE_GTD_IFACE_INDEX	7
+#define PLAT_RESOURCE_MEM_MAX_INDEX	8
 #define PLAT_RESOURCE_ACPI_IO_INDEX	0
 
 /*
@@ -106,8 +108,6 @@
 #define TELEM_SSRAM_SIZE		240
 #define TELEM_PMC_SSRAM_OFFSET		0x1B00
 #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
 /* PMC register bit definitions */
 
@@ -124,26 +124,10 @@ static struct intel_pmc_ipc_dev {
 	int cmd;
 	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
 	spinlock_t gcr_lock;
-
-	/* punit */
-	struct platform_device *punit_dev;
-
-	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
-	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
 } ipcdev;
 
 static char *ipc_err_sources[] = {
@@ -593,44 +577,6 @@ static const struct attribute_group intel_ipc_group = {
 	.attrs = intel_ipc_attrs,
 };
 
-static struct resource punit_res_array[] = {
-	/* Punit BIOS */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	/* Punit ISP */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	/* Punit GTD */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-};
-
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
-	/* ACPI - TCO */
-	{
-		.flags = IORESOURCE_IO,
-	},
-	/* ACPI - SMI */
-	{
-		.flags = IORESOURCE_IO,
-	},
-};
-
 static struct itco_wdt_platform_data tco_info = {
 	.name = "Apollo Lake SoC",
 	.version = 5,
@@ -638,234 +584,179 @@ static struct itco_wdt_platform_data tco_info = {
 	.update_no_reboot_bit = update_no_reboot_bit,
 };
 
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM	0
-#define TELEMETRY_RESOURCE_PMC_SSRAM	1
-static struct resource telemetry_res[] = {
-	/*Telemetry*/
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-};
-
-static int ipc_create_punit_device(void)
+static int ipc_create_punit_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = PUNIT_DEVICE_NAME,
-		.id = -1,
-		.res = punit_res_array,
-		.num_res = ARRAY_SIZE(punit_res_array),
+	struct resource *res;
+	static struct resource punit_res[PLAT_RESOURCE_MEM_MAX_INDEX];
+	static struct mfd_cell punit_cell;
+	int mindex, pindex = 0;
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_MEM_MAX_INDEX; mindex++) {
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, mindex);
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource %d\n",
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if valid resource found, copy the resource to PUNIT
+			 * resource */
+			if (res)
+				memcpy(&punit_res[pindex], res, sizeof(*res));
+			punit_res[pindex].flags = IORESOURCE_MEM;
+			dev_dbg(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_res[pindex]);
+			pindex++;
+			break;
 		};
+	}
 
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	ipcdev.punit_dev = pdev;
+	/* Create PUNIT IPC MFD cell */
+	punit_cell.name = PUNIT_DEVICE_NAME;
+	punit_cell.num_resources = ARRAY_SIZE(punit_res);
+	punit_cell.resources = punit_res;
+	punit_cell.ignore_resource_conflicts = 1;
 
-	return 0;
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&punit_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_tco_device(void)
+static int ipc_create_wdt_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
+	static struct resource wdt_ipc_res[2];
 	struct resource *res;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = TCO_DEVICE_NAME,
-		.id = -1,
-		.res = tco_res,
-		.num_res = ARRAY_SIZE(tco_res),
-		.data = &tco_info,
-		.size_data = sizeof(tco_info),
-		};
+	static struct mfd_cell wdt_cell;
 
-	res = tco_res + TCO_RESOURCE_ACPI_IO;
-	res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
-	res->end = res->start + TCO_REGS_SIZE - 1;
+	/* If we have ACPI based watchdog use that instead, othewise create
+	 * a MFD cell for iTCO watchdog*/
+	if (acpi_has_watchdog())
+		return 0;
 
-	res = tco_res + TCO_RESOURCE_SMI_EN_IO;
-	res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
-	res->end = res->start + SMI_EN_SIZE - 1;
-
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	ipcdev.tco_dev = pdev;
+	/* Get iTCO watchdog resources */
+	res = platform_get_resource(pdev, IORESOURCE_IO,
+				    PLAT_RESOURCE_ACPI_IO_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get wdt resource\n");
+		return -ENXIO;
+	}
 
-	return 0;
+	wdt_ipc_res[0].start = res->start + TCO_BASE_OFFSET;
+	wdt_ipc_res[0].end = res->start + TCO_BASE_OFFSET +
+		TCO_REGS_SIZE - 1;
+	wdt_ipc_res[0].flags = IORESOURCE_IO;
+	wdt_ipc_res[1].start = res->start + SMI_EN_OFFSET;
+	wdt_ipc_res[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;
+	wdt_ipc_res[1].flags = IORESOURCE_IO;
+
+	dev_dbg(&pdev->dev, "watchdog res 0: %pR\n",
+			&wdt_ipc_res[0]);
+	dev_dbg(&pdev->dev, "watchdog res 1: %pR\n",
+			&wdt_ipc_res[1]);
+
+	wdt_cell.name = TCO_DEVICE_NAME;
+	wdt_cell.platform_data = &tco_info;
+	wdt_cell.pdata_size = sizeof(tco_info);
+	wdt_cell.num_resources = ARRAY_SIZE(wdt_ipc_res);
+	wdt_cell.resources = wdt_ipc_res;
+	wdt_cell.ignore_resource_conflicts = 1;
+
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&wdt_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_telemetry_device(void)
+static int ipc_create_telemetry_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
+	static struct resource telemetry_ipc_res[2];
 	struct resource *res;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = TELEMETRY_DEVICE_NAME,
-		.id = -1,
-		.res = telemetry_res,
-		.num_res = ARRAY_SIZE(telemetry_res),
-		};
-
-	res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
-	res->start = ipcdev.telem_punit_ssram_base;
-	res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
-
-	res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
-	res->start = ipcdev.telem_pmc_ssram_base;
-	res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
-
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
+	static struct mfd_cell telemetry_cell;
 
-	ipcdev.telemetry_dev = pdev;
+	/* Get telemetry resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get telemetry resource\n");
+		return -ENXIO;
+	}
 
-	return 0;
+	telemetry_ipc_res[0].start = res->start +
+		TELEM_PUNIT_SSRAM_OFFSET;
+	telemetry_ipc_res[0].end = res->start +
+		TELEM_PUNIT_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+	telemetry_ipc_res[0].flags = IORESOURCE_MEM;
+	telemetry_ipc_res[1].start = res->start + TELEM_PMC_SSRAM_OFFSET;
+	telemetry_ipc_res[1].end = res->start +
+		TELEM_PMC_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+	telemetry_ipc_res[1].flags = IORESOURCE_MEM;
+
+	dev_dbg(&pdev->dev, "Telemetry res 0: %pR\n",
+			&telemetry_ipc_res[0]);
+	dev_dbg(&pdev->dev, "Telemetry res 1: %pR\n",
+			&telemetry_ipc_res[1]);
+
+	telemetry_cell.name = TELEMETRY_DEVICE_NAME;
+	telemetry_cell.num_resources = ARRAY_SIZE(telemetry_ipc_res);
+	telemetry_cell.resources = telemetry_ipc_res;
+	telemetry_cell.ignore_resource_conflicts = 1;
+
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&telemetry_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_pmc_devices(void)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
 {
 	int ret;
 
-	/* If we have ACPI based watchdog use that instead */
-	if (!acpi_has_watchdog()) {
-		ret = ipc_create_tco_device();
-		if (ret) {
-			dev_err(ipcdev.dev, "Failed to add tco platform device\n");
-			return ret;
-		}
-	}
+	ret = ipc_create_punit_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	ret = ipc_create_punit_device();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-		platform_device_unregister(ipcdev.tco_dev);
-	}
+	ret = ipc_create_wdt_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	if (!ipcdev.telem_res_inval) {
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform device\n");
-	}
+	ret = ipc_create_telemetry_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	return ret;
+	return 0;
 }
 
 static int ipc_plat_get_res(struct platform_device *pdev)
 {
-	struct resource *res, *punit_res;
+	struct resource *res;
 	void __iomem *addr;
-	int size;
-
-	res = platform_get_resource(pdev, IORESOURCE_IO,
-				    PLAT_RESOURCE_ACPI_IO_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get io resource\n");
-		return -ENXIO;
-	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
 
-	/* This is index 1 to cover BIOS interface register */
+	/* Get IPC resources */
 	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
+			PLAT_RESOURCE_IPC_INDEX);
 	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
+		dev_err(&pdev->dev, "Failed to get IPC resources\n");
 		return -ENXIO;
 	}
-	*++punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
-	}
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_IPC_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get ipc resource\n");
-		return -ENXIO;
-	}
-	size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
-	res->end = res->start + size - 1;
+	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+			PLAT_RESOURCE_GCR_SIZE - 1);
 
 	addr = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(addr))
 		return PTR_ERR(addr);
 
 	ipcdev.ipc_base = addr;
-
 	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-						TELEM_PUNIT_SSRAM_OFFSET;
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-						TELEM_PMC_SSRAM_OFFSET;
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
-	}
+	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
 
 	return 0;
 }
@@ -921,7 +812,7 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
 		return ret;
@@ -930,38 +821,27 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
 			     "intel_pmc_ipc", &ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto err_irq;
+		return -EBUSY;
 	}
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		goto err_sys;
+		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
+		return ret;
 	}
 
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-err_sys:
-	devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
-err_irq:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
 }
 
 static int ipc_plat_remove(struct platform_device *pdev)
 {
 	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
 	ipcdev.dev = NULL;
+
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 2/8] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently, we have lot of repetitive code in dependent device resource
allocation and device creation handling code. This logic can be improved if
we use MFD framework for dependent device creation. This patch adds this
support.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_pmc_ipc.c | 394 ++++++++++++-----------------------
 1 file changed, 137 insertions(+), 257 deletions(-)

Changes since v4:
 * Changed the order of patches in this patchlist.

Changes since v3:
 * Changed PLATFORM_DEVID_AUTO to PLATFORM_DEVID_NONE in mfd device creation.
 * Fixed error in resource initalization logic in ipc_create_punit_device.
 * Removed mfd cell id initialization.

diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index e03fa314..18e94fa 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -34,6 +34,7 @@
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/spinlock.h>
+#include <linux/mfd/core.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -88,6 +89,7 @@
 #define PLAT_RESOURCE_ISP_IFACE_INDEX	5
 #define PLAT_RESOURCE_GTD_DATA_INDEX	6
 #define PLAT_RESOURCE_GTD_IFACE_INDEX	7
+#define PLAT_RESOURCE_MEM_MAX_INDEX	8
 #define PLAT_RESOURCE_ACPI_IO_INDEX	0
 
 /*
@@ -106,8 +108,6 @@
 #define TELEM_SSRAM_SIZE		240
 #define TELEM_PMC_SSRAM_OFFSET		0x1B00
 #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
 /* PMC register bit definitions */
 
@@ -124,26 +124,10 @@ static struct intel_pmc_ipc_dev {
 	int cmd;
 	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
 	spinlock_t gcr_lock;
-
-	/* punit */
-	struct platform_device *punit_dev;
-
-	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
-	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
 } ipcdev;
 
 static char *ipc_err_sources[] = {
@@ -593,44 +577,6 @@ static const struct attribute_group intel_ipc_group = {
 	.attrs = intel_ipc_attrs,
 };
 
-static struct resource punit_res_array[] = {
-	/* Punit BIOS */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	/* Punit ISP */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	/* Punit GTD */
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-};
-
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
-	/* ACPI - TCO */
-	{
-		.flags = IORESOURCE_IO,
-	},
-	/* ACPI - SMI */
-	{
-		.flags = IORESOURCE_IO,
-	},
-};
-
 static struct itco_wdt_platform_data tco_info = {
 	.name = "Apollo Lake SoC",
 	.version = 5,
@@ -638,234 +584,179 @@ static struct itco_wdt_platform_data tco_info = {
 	.update_no_reboot_bit = update_no_reboot_bit,
 };
 
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM	0
-#define TELEMETRY_RESOURCE_PMC_SSRAM	1
-static struct resource telemetry_res[] = {
-	/*Telemetry*/
-	{
-		.flags = IORESOURCE_MEM,
-	},
-	{
-		.flags = IORESOURCE_MEM,
-	},
-};
-
-static int ipc_create_punit_device(void)
+static int ipc_create_punit_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = PUNIT_DEVICE_NAME,
-		.id = -1,
-		.res = punit_res_array,
-		.num_res = ARRAY_SIZE(punit_res_array),
+	struct resource *res;
+	static struct resource punit_res[PLAT_RESOURCE_MEM_MAX_INDEX];
+	static struct mfd_cell punit_cell;
+	int mindex, pindex = 0;
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_MEM_MAX_INDEX; mindex++) {
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, mindex);
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource %d\n",
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if valid resource found, copy the resource to PUNIT
+			 * resource */
+			if (res)
+				memcpy(&punit_res[pindex], res, sizeof(*res));
+			punit_res[pindex].flags = IORESOURCE_MEM;
+			dev_dbg(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_res[pindex]);
+			pindex++;
+			break;
 		};
+	}
 
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	ipcdev.punit_dev = pdev;
+	/* Create PUNIT IPC MFD cell */
+	punit_cell.name = PUNIT_DEVICE_NAME;
+	punit_cell.num_resources = ARRAY_SIZE(punit_res);
+	punit_cell.resources = punit_res;
+	punit_cell.ignore_resource_conflicts = 1;
 
-	return 0;
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&punit_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_tco_device(void)
+static int ipc_create_wdt_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
+	static struct resource wdt_ipc_res[2];
 	struct resource *res;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = TCO_DEVICE_NAME,
-		.id = -1,
-		.res = tco_res,
-		.num_res = ARRAY_SIZE(tco_res),
-		.data = &tco_info,
-		.size_data = sizeof(tco_info),
-		};
+	static struct mfd_cell wdt_cell;
 
-	res = tco_res + TCO_RESOURCE_ACPI_IO;
-	res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
-	res->end = res->start + TCO_REGS_SIZE - 1;
+	/* If we have ACPI based watchdog use that instead, othewise create
+	 * a MFD cell for iTCO watchdog*/
+	if (acpi_has_watchdog())
+		return 0;
 
-	res = tco_res + TCO_RESOURCE_SMI_EN_IO;
-	res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
-	res->end = res->start + SMI_EN_SIZE - 1;
-
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	ipcdev.tco_dev = pdev;
+	/* Get iTCO watchdog resources */
+	res = platform_get_resource(pdev, IORESOURCE_IO,
+				    PLAT_RESOURCE_ACPI_IO_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get wdt resource\n");
+		return -ENXIO;
+	}
 
-	return 0;
+	wdt_ipc_res[0].start = res->start + TCO_BASE_OFFSET;
+	wdt_ipc_res[0].end = res->start + TCO_BASE_OFFSET +
+		TCO_REGS_SIZE - 1;
+	wdt_ipc_res[0].flags = IORESOURCE_IO;
+	wdt_ipc_res[1].start = res->start + SMI_EN_OFFSET;
+	wdt_ipc_res[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;
+	wdt_ipc_res[1].flags = IORESOURCE_IO;
+
+	dev_dbg(&pdev->dev, "watchdog res 0: %pR\n",
+			&wdt_ipc_res[0]);
+	dev_dbg(&pdev->dev, "watchdog res 1: %pR\n",
+			&wdt_ipc_res[1]);
+
+	wdt_cell.name = TCO_DEVICE_NAME;
+	wdt_cell.platform_data = &tco_info;
+	wdt_cell.pdata_size = sizeof(tco_info);
+	wdt_cell.num_resources = ARRAY_SIZE(wdt_ipc_res);
+	wdt_cell.resources = wdt_ipc_res;
+	wdt_cell.ignore_resource_conflicts = 1;
+
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&wdt_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_telemetry_device(void)
+static int ipc_create_telemetry_device(struct platform_device *pdev)
 {
-	struct platform_device *pdev;
+	static struct resource telemetry_ipc_res[2];
 	struct resource *res;
-	const struct platform_device_info pdevinfo = {
-		.parent = ipcdev.dev,
-		.name = TELEMETRY_DEVICE_NAME,
-		.id = -1,
-		.res = telemetry_res,
-		.num_res = ARRAY_SIZE(telemetry_res),
-		};
-
-	res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
-	res->start = ipcdev.telem_punit_ssram_base;
-	res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
-
-	res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
-	res->start = ipcdev.telem_pmc_ssram_base;
-	res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
-
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
+	static struct mfd_cell telemetry_cell;
 
-	ipcdev.telemetry_dev = pdev;
+	/* Get telemetry resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get telemetry resource\n");
+		return -ENXIO;
+	}
 
-	return 0;
+	telemetry_ipc_res[0].start = res->start +
+		TELEM_PUNIT_SSRAM_OFFSET;
+	telemetry_ipc_res[0].end = res->start +
+		TELEM_PUNIT_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+	telemetry_ipc_res[0].flags = IORESOURCE_MEM;
+	telemetry_ipc_res[1].start = res->start + TELEM_PMC_SSRAM_OFFSET;
+	telemetry_ipc_res[1].end = res->start +
+		TELEM_PMC_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+	telemetry_ipc_res[1].flags = IORESOURCE_MEM;
+
+	dev_dbg(&pdev->dev, "Telemetry res 0: %pR\n",
+			&telemetry_ipc_res[0]);
+	dev_dbg(&pdev->dev, "Telemetry res 1: %pR\n",
+			&telemetry_ipc_res[1]);
+
+	telemetry_cell.name = TELEMETRY_DEVICE_NAME;
+	telemetry_cell.num_resources = ARRAY_SIZE(telemetry_ipc_res);
+	telemetry_cell.resources = telemetry_ipc_res;
+	telemetry_cell.ignore_resource_conflicts = 1;
+
+	return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
+			&telemetry_cell, 1, NULL, 0, NULL);
 }
 
-static int ipc_create_pmc_devices(void)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
 {
 	int ret;
 
-	/* If we have ACPI based watchdog use that instead */
-	if (!acpi_has_watchdog()) {
-		ret = ipc_create_tco_device();
-		if (ret) {
-			dev_err(ipcdev.dev, "Failed to add tco platform device\n");
-			return ret;
-		}
-	}
+	ret = ipc_create_punit_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	ret = ipc_create_punit_device();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-		platform_device_unregister(ipcdev.tco_dev);
-	}
+	ret = ipc_create_wdt_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	if (!ipcdev.telem_res_inval) {
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform device\n");
-	}
+	ret = ipc_create_telemetry_device(pdev);
+	if (ret < 0)
+		return ret;
 
-	return ret;
+	return 0;
 }
 
 static int ipc_plat_get_res(struct platform_device *pdev)
 {
-	struct resource *res, *punit_res;
+	struct resource *res;
 	void __iomem *addr;
-	int size;
-
-	res = platform_get_resource(pdev, IORESOURCE_IO,
-				    PLAT_RESOURCE_ACPI_IO_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get io resource\n");
-		return -ENXIO;
-	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
 
-	/* This is index 1 to cover BIOS interface register */
+	/* Get IPC resources */
 	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
+			PLAT_RESOURCE_IPC_INDEX);
 	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
+		dev_err(&pdev->dev, "Failed to get IPC resources\n");
 		return -ENXIO;
 	}
-	*++punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
-	}
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_IPC_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get ipc resource\n");
-		return -ENXIO;
-	}
-	size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
-	res->end = res->start + size - 1;
+	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+			PLAT_RESOURCE_GCR_SIZE - 1);
 
 	addr = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(addr))
 		return PTR_ERR(addr);
 
 	ipcdev.ipc_base = addr;
-
 	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-						TELEM_PUNIT_SSRAM_OFFSET;
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-						TELEM_PMC_SSRAM_OFFSET;
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
-	}
+	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
 
 	return 0;
 }
@@ -921,7 +812,7 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
 		return ret;
@@ -930,38 +821,27 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
 			     "intel_pmc_ipc", &ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto err_irq;
+		return -EBUSY;
 	}
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		goto err_sys;
+		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
+		return ret;
 	}
 
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-err_sys:
-	devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
-err_irq:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
 }
 
 static int ipc_plat_remove(struct platform_device *pdev)
 {
 	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
 	ipcdev.dev = NULL;
+
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 3/8] platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

This patch adds support for regmap based implementation for GCR
read/write/update APIs.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/Kconfig         |   1 +
 drivers/platform/x86/intel_pmc_ipc.c | 120 +++++++++++++----------------------
 2 files changed, 44 insertions(+), 77 deletions(-)

Changes since v4:
 * None

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 1f7959f..da2d9ba 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1067,6 +1067,7 @@ config PVPANIC
 config INTEL_PMC_IPC
 	tristate "Intel PMC IPC Driver"
 	depends on ACPI
+	select REGMAP_MMIO
 	---help---
 	This driver provides support for PMC control on some Intel platforms.
 	The PMC is an ARC processor which defines IPC commands for communication
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 18e94fa..fd5bcdf 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -35,6 +35,7 @@
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/spinlock.h>
 #include <linux/mfd/core.h>
+#include <linux/regmap.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -126,8 +127,7 @@ static struct intel_pmc_ipc_dev {
 
 	/* gcr */
 	void __iomem *gcr_mem_base;
-	bool has_gcr_regs;
-	spinlock_t gcr_lock;
+	struct regmap *gcr_regs;
 } ipcdev;
 
 static char *ipc_err_sources[] = {
@@ -149,6 +149,15 @@ static char *ipc_err_sources[] = {
 		"Unsigned kernel",
 };
 
+static struct regmap_config gcr_regmap_config = {
+	.name = "intel_pmc_gcr",
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
+	.max_register = PLAT_RESOURCE_GCR_SIZE,
+};
+
 /* Prevent concurrent calls to the PMC */
 static DEFINE_MUTEX(ipclock);
 
@@ -182,21 +191,6 @@ static inline u32 ipc_data_readl(u32 offset)
 	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
 }
 
-static inline u64 gcr_data_readq(u32 offset)
-{
-	return readq(ipcdev.gcr_mem_base + offset);
-}
-
-static inline int is_gcr_valid(u32 offset)
-{
-	if (!ipcdev.has_gcr_regs)
-		return -EACCES;
-
-	if (offset > PLAT_RESOURCE_GCR_SIZE)
-		return -EINVAL;
-
-	return 0;
-}
 
 /**
  * intel_pmc_gcr_read() - Read PMC GCR register
@@ -209,21 +203,12 @@ static inline int is_gcr_valid(u32 offset)
  */
 int intel_pmc_gcr_read(u32 offset, u32 *data)
 {
-	int ret;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0) {
-		spin_unlock(&ipcdev.gcr_lock);
-		return ret;
-	}
-
-	*data = readl(ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	spin_unlock(&ipcdev.gcr_lock);
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-	return 0;
+	return regmap_read(pmc->gcr_regs, offset, data);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
 
@@ -239,21 +224,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
  */
 int intel_pmc_gcr_write(u32 offset, u32 data)
 {
-	int ret;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0) {
-		spin_unlock(&ipcdev.gcr_lock);
-		return ret;
-	}
-
-	writel(data, ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	spin_unlock(&ipcdev.gcr_lock);
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-	return 0;
+	return regmap_write(pmc->gcr_regs, offset, data);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
 
@@ -270,33 +246,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
  */
 int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 {
-	u32 new_val;
-	int ret = 0;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0)
-		goto gcr_ipc_unlock;
-
-	new_val = readl(ipcdev.gcr_mem_base + offset);
-
-	new_val &= ~mask;
-	new_val |= val & mask;
-
-	writel(new_val, ipcdev.gcr_mem_base + offset);
-
-	new_val = readl(ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	/* check whether the bit update is successful */
-	if ((new_val & mask) != (val & mask)) {
-		ret = -EIO;
-		goto gcr_ipc_unlock;
-	}
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-gcr_ipc_unlock:
-	spin_unlock(&ipcdev.gcr_lock);
-	return ret;
+	return regmap_update_bits(pmc->gcr_regs, offset, mask, val);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
 
@@ -475,8 +430,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
 
-	spin_lock_init(&ipcdev.gcr_lock);
-
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -769,17 +722,26 @@ static int ipc_plat_get_res(struct platform_device *pdev)
  */
 int intel_pmc_s0ix_counter_read(u64 *data)
 {
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 	u64 deep, shlw;
+	int ret;
 
-	if (!ipcdev.has_gcr_regs)
+	if (!pmc->gcr_regs)
 		return -EACCES;
 
-	deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
-	shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
+	ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_DEEP_S0IX_REG,
+			       &deep, 2);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_SHLW_S0IX_REG,
+			       &shlw, 2);
+	if (ret)
+		return ret;
 
 	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
 
@@ -798,7 +760,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	ipcdev.dev = &pdev->dev;
 	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
 	init_completion(&ipcdev.cmd_complete);
-	spin_lock_init(&ipcdev.gcr_lock);
 
 	ipcdev.irq = platform_get_irq(pdev, 0);
 	if (ipcdev.irq < 0) {
@@ -812,6 +773,13 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
+			ipcdev.gcr_mem_base, &gcr_regmap_config);
+        if (IS_ERR(ipcdev.gcr_regs)) {
+                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
+                return PTR_ERR(ipcdev.gcr_regs);;
+        }
+
 	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
@@ -832,8 +800,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ipcdev.has_gcr_regs = true;
-
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 3/8] platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

This patch adds support for regmap based implementation for GCR
read/write/update APIs.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/Kconfig         |   1 +
 drivers/platform/x86/intel_pmc_ipc.c | 120 +++++++++++++----------------------
 2 files changed, 44 insertions(+), 77 deletions(-)

Changes since v4:
 * None

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 1f7959f..da2d9ba 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1067,6 +1067,7 @@ config PVPANIC
 config INTEL_PMC_IPC
 	tristate "Intel PMC IPC Driver"
 	depends on ACPI
+	select REGMAP_MMIO
 	---help---
 	This driver provides support for PMC control on some Intel platforms.
 	The PMC is an ARC processor which defines IPC commands for communication
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 18e94fa..fd5bcdf 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -35,6 +35,7 @@
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/spinlock.h>
 #include <linux/mfd/core.h>
+#include <linux/regmap.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -126,8 +127,7 @@ static struct intel_pmc_ipc_dev {
 
 	/* gcr */
 	void __iomem *gcr_mem_base;
-	bool has_gcr_regs;
-	spinlock_t gcr_lock;
+	struct regmap *gcr_regs;
 } ipcdev;
 
 static char *ipc_err_sources[] = {
@@ -149,6 +149,15 @@ static char *ipc_err_sources[] = {
 		"Unsigned kernel",
 };
 
+static struct regmap_config gcr_regmap_config = {
+	.name = "intel_pmc_gcr",
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
+	.max_register = PLAT_RESOURCE_GCR_SIZE,
+};
+
 /* Prevent concurrent calls to the PMC */
 static DEFINE_MUTEX(ipclock);
 
@@ -182,21 +191,6 @@ static inline u32 ipc_data_readl(u32 offset)
 	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
 }
 
-static inline u64 gcr_data_readq(u32 offset)
-{
-	return readq(ipcdev.gcr_mem_base + offset);
-}
-
-static inline int is_gcr_valid(u32 offset)
-{
-	if (!ipcdev.has_gcr_regs)
-		return -EACCES;
-
-	if (offset > PLAT_RESOURCE_GCR_SIZE)
-		return -EINVAL;
-
-	return 0;
-}
 
 /**
  * intel_pmc_gcr_read() - Read PMC GCR register
@@ -209,21 +203,12 @@ static inline int is_gcr_valid(u32 offset)
  */
 int intel_pmc_gcr_read(u32 offset, u32 *data)
 {
-	int ret;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0) {
-		spin_unlock(&ipcdev.gcr_lock);
-		return ret;
-	}
-
-	*data = readl(ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	spin_unlock(&ipcdev.gcr_lock);
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-	return 0;
+	return regmap_read(pmc->gcr_regs, offset, data);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
 
@@ -239,21 +224,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
  */
 int intel_pmc_gcr_write(u32 offset, u32 data)
 {
-	int ret;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0) {
-		spin_unlock(&ipcdev.gcr_lock);
-		return ret;
-	}
-
-	writel(data, ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	spin_unlock(&ipcdev.gcr_lock);
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-	return 0;
+	return regmap_write(pmc->gcr_regs, offset, data);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
 
@@ -270,33 +246,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
  */
 int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
 {
-	u32 new_val;
-	int ret = 0;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0)
-		goto gcr_ipc_unlock;
-
-	new_val = readl(ipcdev.gcr_mem_base + offset);
-
-	new_val &= ~mask;
-	new_val |= val & mask;
-
-	writel(new_val, ipcdev.gcr_mem_base + offset);
-
-	new_val = readl(ipcdev.gcr_mem_base + offset);
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 
-	/* check whether the bit update is successful */
-	if ((new_val & mask) != (val & mask)) {
-		ret = -EIO;
-		goto gcr_ipc_unlock;
-	}
+	if (!pmc->gcr_regs)
+		return -EACCES;
 
-gcr_ipc_unlock:
-	spin_unlock(&ipcdev.gcr_lock);
-	return ret;
+	return regmap_update_bits(pmc->gcr_regs, offset, mask, val);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
 
@@ -475,8 +430,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
 
-	spin_lock_init(&ipcdev.gcr_lock);
-
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -769,17 +722,26 @@ static int ipc_plat_get_res(struct platform_device *pdev)
  */
 int intel_pmc_s0ix_counter_read(u64 *data)
 {
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
 	u64 deep, shlw;
+	int ret;
 
-	if (!ipcdev.has_gcr_regs)
+	if (!pmc->gcr_regs)
 		return -EACCES;
 
-	deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
-	shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
+	ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_DEEP_S0IX_REG,
+			       &deep, 2);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_SHLW_S0IX_REG,
+			       &shlw, 2);
+	if (ret)
+		return ret;
 
 	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
 
@@ -798,7 +760,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	ipcdev.dev = &pdev->dev;
 	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
 	init_completion(&ipcdev.cmd_complete);
-	spin_lock_init(&ipcdev.gcr_lock);
 
 	ipcdev.irq = platform_get_irq(pdev, 0);
 	if (ipcdev.irq < 0) {
@@ -812,6 +773,13 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
+			ipcdev.gcr_mem_base, &gcr_regmap_config);
+        if (IS_ERR(ipcdev.gcr_regs)) {
+                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
+                return PTR_ERR(ipcdev.gcr_regs);;
+        }
+
 	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
@@ -832,8 +800,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ipcdev.has_gcr_regs = true;
-
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
  (?)
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
redundantly implements the same IPC features and has lot of code
duplication between them. This driver addresses this issue by grouping
the common IPC functionalities under the same driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/Kconfig                    |   8 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 ++++++++++++++++++++++++
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
 4 files changed, 791 insertions(+)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

Changes since v4:
 * None

Changes since v3:
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * Fixed error in check for duplicate intel_ipc_dev.
 * Added custom interrupt handler support.
 * Used char array for error string conversion.
 * Added put dev support.
 * Added devm_* variant of intel_ipc_dev_get().

Changes since v2:
 * Added ipc_dev_cmd API support.

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index da2d9ba..724ee696 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1153,6 +1153,14 @@ config SILEAD_DMI
 	  with the OS-image for the device. This option supplies the missing
 	  information. Enable this for x86 tablets with Silead touchscreens.
 
+config INTEL_IPC_DEV
+	bool "Intel IPC Device Driver"
+	depends on X86_64
+	---help---
+	  This driver implements core features of Intel IPC device. Devices
+	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
+	  driver to implement IPC protocol of their respective device.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2b315d0..99a1af1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+= pmc_atom.o
 obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
 obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
 obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
+obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
diff --git a/drivers/platform/x86/intel_ipc_dev.c b/drivers/platform/x86/intel_ipc_dev.c
new file mode 100644
index 0000000..f55ddec
--- /dev/null
+++ b/drivers/platform/x86/intel_ipc_dev.c
@@ -0,0 +1,576 @@
+/*
+ * intel_ipc_dev.c: Intel IPC device class driver
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+#include <linux/regmap.h>
+
+/* mutex to sync different ipc devices in same channel */
+static struct mutex channel_lock[IPC_CHANNEL_MAX];
+
+static char *ipc_err_sources[] = {
+	[IPC_DEV_ERR_NONE] =
+		"No error",
+	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
+		"Command not-supported/Invalid",
+	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
+		"Command not-serviced/Invalid param",
+	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
+		"Unable-to-service/Cmd-timeout",
+	[IPC_DEV_ERR_CMD_INVALID] =
+		"Command-invalid/Cmd-locked",
+	[IPC_DEV_ERR_CMD_FAILED] =
+		"Command-failed/Invalid-VR-id",
+	[IPC_DEV_ERR_EMSECURITY] =
+		"Invalid Battery/VR-Error",
+	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
+		"Unsigned kernel",
+};
+
+static void ipc_channel_lock_init(void)
+{
+	int i;
+
+	for (i = 0; i < IPC_CHANNEL_MAX; i++)
+		mutex_init(&channel_lock[i]);
+}
+
+static struct class intel_ipc_class = {
+	.name = "intel_ipc",
+	.owner = THIS_MODULE,
+};
+
+static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* acquire channel lock */
+	mutex_lock(&channel_lock[chan_type]);
+
+	/* acquire IPC device lock */
+	mutex_lock(&ipc_dev->lock);
+
+	return 0;
+}
+
+static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* release IPC device lock */
+	mutex_unlock(&ipc_dev->lock);
+
+	/* release channel lock */
+	mutex_unlock(&channel_lock[chan_type]);
+
+	return 0;
+}
+
+static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
+	int error)
+{
+	if (error < IPC_DEV_ERR_MAX)
+		return ipc_err_sources[error];
+
+	return "Unknown Command";
+}
+
+/* Helper function to send given command to IPC device */
+static inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)
+{
+	ipc_dev->cmd = cmd;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
+		reinit_completion(&ipc_dev->cmd_complete);
+
+	if (ipc_dev->ops->enable_msi)
+		cmd = ipc_dev->ops->enable_msi(cmd);
+
+	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
+}
+
+static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev)
+{
+	int status;
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->busy_check)
+		return ipc_dev->ops->busy_check(status);
+
+	return 0;
+}
+
+/* Check the status of IPC command and return err code if failed */
+static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev)
+{
+	int loop_count = IPC_DEV_CMD_LOOP_CNT;
+	int status;
+	int ret = 0;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
+				IPC_DEV_CMD_TIMEOUT))
+			ret = -ETIMEDOUT;
+	} else {
+		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
+			udelay(1);
+		if (!loop_count)
+			ret = -ETIMEDOUT;
+	}
+
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev,
+				"IPC timed out, CMD=0x%x\n", ipc_dev->cmd);
+		return ret;
+	}
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->to_err_code)
+		ret = ipc_dev->ops->to_err_code(status);
+
+	if (ret) {
+		dev_err(&ipc_dev->dev,
+				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
+				ipc_dev_err_string(ipc_dev, ret),
+				status, ipc_dev->cmd);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * ipc_dev_simple_cmd() - Send simple IPC command
+ * @ipc_dev     : Reference to ipc device.
+ * @cmd_list    : IPC command list.
+ * @cmdlen      : Number of cmd/sub-cmds.
+ *
+ * Send a simple IPC command to ipc device.
+ * Use this when don't need to specify input/output data
+ * and source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen)
+{
+	int ret;
+
+	if (!cmd_list)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler */
+	if (ipc_dev->ops->pre_simple_cmd_fn) {
+		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
+
+/**
+ * ipc_dev_cmd() - Send IPC command with data.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in dwords.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ *
+ * Send an IPC command to device with input/output data.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	int ret;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_cmd_fn) {
+		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, in, inlen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_cmd);
+
+/**
+ * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in bytes.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ * @dptr        : IPC destination data address.
+ * @sptr        : IPC source data address.
+ *
+ * Send an IPC command to device with input/output data and
+ * source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	int ret;
+	int inbuflen = DIV_ROUND_UP(inlen, 4);
+	u32 *inbuf;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	inbuf = kzalloc(inbuflen, GFP_KERNEL);
+	if (!inbuf)
+		return -ENOMEM;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_raw_cmd_fn) {
+		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen, dptr, sptr);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* If supported, write DPTR register.*/
+	if (ipc_dev->cfg->support_dptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->dptr_reg,
+				dptr);
+
+	/* If supported, write SPTR register. */
+	if (ipc_dev->cfg->support_sptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->sptr_reg,
+				sptr);
+
+	memcpy(inbuf, in, inlen);
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+	kfree(inbuf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
+
+/* sysfs option to send simple IPC commands from userspace */
+static ssize_t ipc_dev_cmd_reg_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
+	u32 cmd;
+	int ret;
+
+	ret = sscanf(buf, "%d", &cmd);
+	if (ret != 1) {
+		dev_err(dev, "Error args\n");
+		return -EINVAL;
+	}
+
+	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
+	if (ret) {
+		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
+		return ret;
+	}
+	return (ssize_t)count;
+}
+
+static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
+
+static struct attribute *ipc_dev_attrs[] = {
+	&dev_attr_send_cmd.attr,
+	NULL
+};
+
+static const struct attribute_group ipc_dev_group = {
+	.attrs = ipc_dev_attrs,
+};
+
+static const struct attribute_group *ipc_dev_groups[] = {
+	&ipc_dev_group,
+	NULL,
+};
+
+/* IPC device IRQ handler */
+static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id)
+{
+	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
+
+	if (ipc_dev->ops->pre_irq_handler_fn)
+		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
+
+	complete(&ipc_dev->cmd_complete);
+
+	return IRQ_HANDLED;
+}
+
+static void devm_intel_ipc_dev_release(struct device *dev, void *res)
+{
+	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
+
+	if (!ipc_dev)
+		return;
+
+	device_del(&ipc_dev->dev);
+
+	kfree(ipc_dev);
+}
+
+static int match_name(struct device *dev, const void *data)
+{
+        if (!dev_name(dev))
+                return 0;
+
+        return !strcmp(dev_name(dev), (char *)data);
+}
+
+/**
+ * intel_ipc_dev_get() - Get Intel IPC device from name.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+        struct device *dev;
+
+	if (!dev_name)
+		return ERR_PTR(-EINVAL);
+
+	dev = class_find_device(&intel_ipc_class, NULL, dev_name, match_name);
+
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
+
+static void devm_intel_ipc_dev_put(struct device *dev, void *res)
+{
+	intel_ipc_dev_put(*(struct intel_ipc_dev **)res);
+}
+
+/**
+ * devm_intel_ipc_dev_get() - Resource managed version of intel_ipc_dev_get().
+ * @dev         : Device pointer.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+
+	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = intel_ipc_dev_get(dev_name);
+	if (!IS_ERR_OR_NULL(ipc_dev)) {
+		*ptr = ipc_dev;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ipc_dev;
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
+
+/**
+ * devm_intel_ipc_dev_create() - Create IPC device
+ * @dev		: IPC parent device.
+ * @devname	: Name of the IPC device.
+ * @cfg		: IPC device configuration.
+ * @ops		: IPC device ops.
+ *
+ * Resource managed API to create IPC device with
+ * given configuration.
+ *
+ * Return	: IPC device pointer or ERR_PTR(error code).
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname,
+		struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+	int ret;
+
+	if (!dev && !devname && !cfg)
+		return ERR_PTR(-EINVAL);
+
+	if (intel_ipc_dev_get(devname)) {
+		dev_err(dev, "IPC device %s already exist\n", devname);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
+			GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
+	if (!ipc_dev) {
+		ret = -ENOMEM;
+		goto err_dev_create;
+	}
+
+	ipc_dev->dev.class = &intel_ipc_class;
+	ipc_dev->dev.parent = dev;
+	ipc_dev->dev.groups = ipc_dev_groups;
+	ipc_dev->cfg = cfg;
+	ipc_dev->ops = ops;
+
+	mutex_init(&ipc_dev->lock);
+	init_completion(&ipc_dev->cmd_complete);
+	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
+	dev_set_name(&ipc_dev->dev, devname);
+	device_initialize(&ipc_dev->dev);
+
+	ret = device_add(&ipc_dev->dev);
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev, "%s device create failed\n",
+				__func__);
+		ret = -ENODEV;
+		goto err_dev_add;
+	}
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (devm_request_irq(&ipc_dev->dev,
+				ipc_dev->cfg->irq,
+				ipc_dev_irq_handler,
+				ipc_dev->cfg->irqflags,
+				dev_name(&ipc_dev->dev),
+				ipc_dev)) {
+			dev_err(&ipc_dev->dev,
+					"Failed to request irq\n");
+			goto err_irq_request;
+		}
+	}
+
+	*ptr = ipc_dev;
+
+	devres_add(dev, ptr);
+
+	return ipc_dev;
+
+err_irq_request:
+	device_del(&ipc_dev->dev);
+err_dev_add:
+	kfree(ipc_dev);
+err_dev_create:
+	devres_free(ptr);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
+
+static int __init intel_ipc_init(void)
+{
+	ipc_channel_lock_init();
+	return class_register(&intel_ipc_class);
+}
+
+static void __exit intel_ipc_exit(void)
+{
+	class_unregister(&intel_ipc_class);
+}
+subsys_initcall(intel_ipc_init);
+module_exit(intel_ipc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kuppuswamy Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
+MODULE_DESCRIPTION("Intel IPC device class driver");
diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h b/include/linux/platform_data/x86/intel_ipc_dev.h
new file mode 100644
index 0000000..eaeedaf
--- /dev/null
+++ b/include/linux/platform_data/x86/intel_ipc_dev.h
@@ -0,0 +1,206 @@
+/*
+ * Intel IPC class device header file.
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#ifndef INTEL_IPC_DEV_H
+#define INTEL_IPC_DEV_H
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+/* IPC channel type */
+#define IPC_CHANNEL_IA_PMC                      0
+#define IPC_CHANNEL_IA_PUNIT                    1
+#define IPC_CHANNEL_PMC_PUNIT                   2
+#define IPC_CHANNEL_IA_SCU                      3
+#define IPC_CHANNEL_MAX                         4
+
+/* IPC return code */
+#define IPC_DEV_ERR_NONE			0
+#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
+#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
+#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
+#define IPC_DEV_ERR_CMD_INVALID			4
+#define IPC_DEV_ERR_CMD_FAILED			5
+#define IPC_DEV_ERR_EMSECURITY			6
+#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
+#define IPC_DEV_ERR_MAX				8
+
+/* IPC mode */
+#define IPC_DEV_MODE_IRQ			0
+#define IPC_DEV_MODE_POLLING			1
+
+/* IPC dev constants */
+#define IPC_DEV_CMD_LOOP_CNT			3000000
+#define IPC_DEV_CMD_TIMEOUT			3 * HZ
+#define IPC_DEV_DATA_BUFFER_SIZE		16
+
+struct intel_ipc_dev;
+struct intel_ipc_raw_cmd;
+
+/**
+ * struct intel_ipc_dev_cfg - IPC device config structure.
+ *
+ * IPC device drivers uses the following config options to
+ * register new IPC device.
+ *
+ * @cmd_regs            : IPC device command base regmap.
+ * @data_regs           : IPC device data base regmap.
+ * @wrbuf_reg           : IPC device data write register address.
+ * @rbuf_reg            : IPC device data read register address.
+ * @sptr_reg            : IPC device source data pointer register address.
+ * @dptr_reg            : IPC device destination data pointer register
+ *                        address.
+ * @status_reg          : IPC command status register address.
+ * @cmd_reg             : IPC command register address.
+ * @mode                : IRQ/POLLING mode.
+ * @irq                 : IPC device IRQ number.
+ * @irqflags            : IPC device IRQ flags.
+ * @chan_type           : IPC device channel type(PMC/PUNIT).
+ * @msi                 : Enable/Disable MSI for IPC commands.
+ * @support_dptr        : Support DPTR update.
+ * @support_sptr        : Support SPTR update.
+ *
+ */
+struct intel_ipc_dev_cfg {
+	struct regmap *cmd_regs;
+	struct regmap *data_regs;
+	unsigned int wrbuf_reg;
+	unsigned int rbuf_reg;
+	unsigned int sptr_reg;
+	unsigned int dptr_reg;
+	unsigned int status_reg;
+	unsigned int cmd_reg;
+	int mode;
+	int irq;
+	int irqflags;
+	int chan_type;
+	bool use_msi;
+	bool support_dptr;
+	bool support_sptr;
+};
+
+/**
+ * struct intel_ipc_dev_ops - IPC device ops structure.
+ *
+ * Call backs for IPC device specific operations.
+ *
+ * @to_err_code         : Status to error code conversion function.
+ * @busy_check          : Check for IPC busy status.
+ * @enable_msi          : Enable MSI for IPC commands.
+ * @pre_simple_cmd_fn   : Custom pre-processing function for
+ *                        ipc_dev_simple_cmd()
+ * @pre_cmd_fn          : Custom pre-processing function for
+ *                        ipc_dev_cmd()
+ * @pre_raw_cmd_fn      : Custom pre-processing function for
+ *                        ipc_dev_raw_cmd()
+ *
+ */
+struct intel_ipc_dev_ops {
+	int (*to_err_code)(int status);
+	int (*busy_check)(int status);
+	u32 (*enable_msi)(u32 cmd);
+	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
+	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
+			u32 *out, u32 outlen);
+	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+			u32 *out, u32 outlen, u32 dptr, u32 sptr);
+	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq);
+};
+
+/**
+ * struct intel_ipc_dev - Intel IPC device structure.
+ *
+ * Used with devm_intel_ipc_dev_create() to create new IPC device.
+ *
+ * @dev                 : IPC device object.
+ * @cmd                 : Current IPC device command.
+ * @cmd_complete        : Command completion object.
+ * @lock                : Lock to protect IPC device structure.
+ * @ops                 : IPC device ops pointer.
+ * @cfg                 : IPC device cfg pointer.
+ *
+ */
+struct intel_ipc_dev {
+	struct device dev;
+	int cmd;
+	struct completion cmd_complete;
+	struct mutex lock;
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+};
+
+#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
+
+/* API to create new IPC device */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops);
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen);
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen);
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name);
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	put_device(&ipc_dev->dev);
+}
+#else
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
+		struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
+		u32 *cmd_list, u32 cmdlen)
+{
+	return -EINVAL;
+}
+
+static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
+		u32 dptr, u32 sptr);
+{
+	return -EINVAL;
+}
+
+static inline struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+	return NULL;
+}
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+{
+	return NULL;
+}
+
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	return NULL;
+}
+#endif /* CONFIG_INTEL_IPC_DEV */
+#endif /* INTEL_IPC_DEV_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
redundantly implements the same IPC features and has lot of code
duplication between them. This driver addresses this issue by grouping
the common IPC functionalities under the same driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/Kconfig                    |   8 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 ++++++++++++++++++++++++
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
 4 files changed, 791 insertions(+)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

Changes since v4:
 * None

Changes since v3:
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * Fixed error in check for duplicate intel_ipc_dev.
 * Added custom interrupt handler support.
 * Used char array for error string conversion.
 * Added put dev support.
 * Added devm_* variant of intel_ipc_dev_get().

Changes since v2:
 * Added ipc_dev_cmd API support.

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index da2d9ba..724ee696 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1153,6 +1153,14 @@ config SILEAD_DMI
 	  with the OS-image for the device. This option supplies the missing
 	  information. Enable this for x86 tablets with Silead touchscreens.
 
+config INTEL_IPC_DEV
+	bool "Intel IPC Device Driver"
+	depends on X86_64
+	---help---
+	  This driver implements core features of Intel IPC device. Devices
+	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
+	  driver to implement IPC protocol of their respective device.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2b315d0..99a1af1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+= pmc_atom.o
 obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
 obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
 obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
+obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
diff --git a/drivers/platform/x86/intel_ipc_dev.c b/drivers/platform/x86/intel_ipc_dev.c
new file mode 100644
index 0000000..f55ddec
--- /dev/null
+++ b/drivers/platform/x86/intel_ipc_dev.c
@@ -0,0 +1,576 @@
+/*
+ * intel_ipc_dev.c: Intel IPC device class driver
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+#include <linux/regmap.h>
+
+/* mutex to sync different ipc devices in same channel */
+static struct mutex channel_lock[IPC_CHANNEL_MAX];
+
+static char *ipc_err_sources[] = {
+	[IPC_DEV_ERR_NONE] =
+		"No error",
+	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
+		"Command not-supported/Invalid",
+	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
+		"Command not-serviced/Invalid param",
+	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
+		"Unable-to-service/Cmd-timeout",
+	[IPC_DEV_ERR_CMD_INVALID] =
+		"Command-invalid/Cmd-locked",
+	[IPC_DEV_ERR_CMD_FAILED] =
+		"Command-failed/Invalid-VR-id",
+	[IPC_DEV_ERR_EMSECURITY] =
+		"Invalid Battery/VR-Error",
+	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
+		"Unsigned kernel",
+};
+
+static void ipc_channel_lock_init(void)
+{
+	int i;
+
+	for (i = 0; i < IPC_CHANNEL_MAX; i++)
+		mutex_init(&channel_lock[i]);
+}
+
+static struct class intel_ipc_class = {
+	.name = "intel_ipc",
+	.owner = THIS_MODULE,
+};
+
+static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* acquire channel lock */
+	mutex_lock(&channel_lock[chan_type]);
+
+	/* acquire IPC device lock */
+	mutex_lock(&ipc_dev->lock);
+
+	return 0;
+}
+
+static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* release IPC device lock */
+	mutex_unlock(&ipc_dev->lock);
+
+	/* release channel lock */
+	mutex_unlock(&channel_lock[chan_type]);
+
+	return 0;
+}
+
+static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
+	int error)
+{
+	if (error < IPC_DEV_ERR_MAX)
+		return ipc_err_sources[error];
+
+	return "Unknown Command";
+}
+
+/* Helper function to send given command to IPC device */
+static inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)
+{
+	ipc_dev->cmd = cmd;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
+		reinit_completion(&ipc_dev->cmd_complete);
+
+	if (ipc_dev->ops->enable_msi)
+		cmd = ipc_dev->ops->enable_msi(cmd);
+
+	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
+}
+
+static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev)
+{
+	int status;
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->busy_check)
+		return ipc_dev->ops->busy_check(status);
+
+	return 0;
+}
+
+/* Check the status of IPC command and return err code if failed */
+static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev)
+{
+	int loop_count = IPC_DEV_CMD_LOOP_CNT;
+	int status;
+	int ret = 0;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
+				IPC_DEV_CMD_TIMEOUT))
+			ret = -ETIMEDOUT;
+	} else {
+		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
+			udelay(1);
+		if (!loop_count)
+			ret = -ETIMEDOUT;
+	}
+
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev,
+				"IPC timed out, CMD=0x%x\n", ipc_dev->cmd);
+		return ret;
+	}
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->to_err_code)
+		ret = ipc_dev->ops->to_err_code(status);
+
+	if (ret) {
+		dev_err(&ipc_dev->dev,
+				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
+				ipc_dev_err_string(ipc_dev, ret),
+				status, ipc_dev->cmd);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * ipc_dev_simple_cmd() - Send simple IPC command
+ * @ipc_dev     : Reference to ipc device.
+ * @cmd_list    : IPC command list.
+ * @cmdlen      : Number of cmd/sub-cmds.
+ *
+ * Send a simple IPC command to ipc device.
+ * Use this when don't need to specify input/output data
+ * and source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen)
+{
+	int ret;
+
+	if (!cmd_list)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler */
+	if (ipc_dev->ops->pre_simple_cmd_fn) {
+		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
+
+/**
+ * ipc_dev_cmd() - Send IPC command with data.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in dwords.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ *
+ * Send an IPC command to device with input/output data.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	int ret;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_cmd_fn) {
+		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, in, inlen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_cmd);
+
+/**
+ * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in bytes.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ * @dptr        : IPC destination data address.
+ * @sptr        : IPC source data address.
+ *
+ * Send an IPC command to device with input/output data and
+ * source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	int ret;
+	int inbuflen = DIV_ROUND_UP(inlen, 4);
+	u32 *inbuf;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	inbuf = kzalloc(inbuflen, GFP_KERNEL);
+	if (!inbuf)
+		return -ENOMEM;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_raw_cmd_fn) {
+		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen, dptr, sptr);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* If supported, write DPTR register.*/
+	if (ipc_dev->cfg->support_dptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->dptr_reg,
+				dptr);
+
+	/* If supported, write SPTR register. */
+	if (ipc_dev->cfg->support_sptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->sptr_reg,
+				sptr);
+
+	memcpy(inbuf, in, inlen);
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+	kfree(inbuf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
+
+/* sysfs option to send simple IPC commands from userspace */
+static ssize_t ipc_dev_cmd_reg_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
+	u32 cmd;
+	int ret;
+
+	ret = sscanf(buf, "%d", &cmd);
+	if (ret != 1) {
+		dev_err(dev, "Error args\n");
+		return -EINVAL;
+	}
+
+	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
+	if (ret) {
+		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
+		return ret;
+	}
+	return (ssize_t)count;
+}
+
+static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
+
+static struct attribute *ipc_dev_attrs[] = {
+	&dev_attr_send_cmd.attr,
+	NULL
+};
+
+static const struct attribute_group ipc_dev_group = {
+	.attrs = ipc_dev_attrs,
+};
+
+static const struct attribute_group *ipc_dev_groups[] = {
+	&ipc_dev_group,
+	NULL,
+};
+
+/* IPC device IRQ handler */
+static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id)
+{
+	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
+
+	if (ipc_dev->ops->pre_irq_handler_fn)
+		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
+
+	complete(&ipc_dev->cmd_complete);
+
+	return IRQ_HANDLED;
+}
+
+static void devm_intel_ipc_dev_release(struct device *dev, void *res)
+{
+	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
+
+	if (!ipc_dev)
+		return;
+
+	device_del(&ipc_dev->dev);
+
+	kfree(ipc_dev);
+}
+
+static int match_name(struct device *dev, const void *data)
+{
+        if (!dev_name(dev))
+                return 0;
+
+        return !strcmp(dev_name(dev), (char *)data);
+}
+
+/**
+ * intel_ipc_dev_get() - Get Intel IPC device from name.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+        struct device *dev;
+
+	if (!dev_name)
+		return ERR_PTR(-EINVAL);
+
+	dev = class_find_device(&intel_ipc_class, NULL, dev_name, match_name);
+
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
+
+static void devm_intel_ipc_dev_put(struct device *dev, void *res)
+{
+	intel_ipc_dev_put(*(struct intel_ipc_dev **)res);
+}
+
+/**
+ * devm_intel_ipc_dev_get() - Resource managed version of intel_ipc_dev_get().
+ * @dev         : Device pointer.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+
+	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = intel_ipc_dev_get(dev_name);
+	if (!IS_ERR_OR_NULL(ipc_dev)) {
+		*ptr = ipc_dev;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ipc_dev;
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
+
+/**
+ * devm_intel_ipc_dev_create() - Create IPC device
+ * @dev		: IPC parent device.
+ * @devname	: Name of the IPC device.
+ * @cfg		: IPC device configuration.
+ * @ops		: IPC device ops.
+ *
+ * Resource managed API to create IPC device with
+ * given configuration.
+ *
+ * Return	: IPC device pointer or ERR_PTR(error code).
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname,
+		struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+	int ret;
+
+	if (!dev && !devname && !cfg)
+		return ERR_PTR(-EINVAL);
+
+	if (intel_ipc_dev_get(devname)) {
+		dev_err(dev, "IPC device %s already exist\n", devname);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
+			GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
+	if (!ipc_dev) {
+		ret = -ENOMEM;
+		goto err_dev_create;
+	}
+
+	ipc_dev->dev.class = &intel_ipc_class;
+	ipc_dev->dev.parent = dev;
+	ipc_dev->dev.groups = ipc_dev_groups;
+	ipc_dev->cfg = cfg;
+	ipc_dev->ops = ops;
+
+	mutex_init(&ipc_dev->lock);
+	init_completion(&ipc_dev->cmd_complete);
+	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
+	dev_set_name(&ipc_dev->dev, devname);
+	device_initialize(&ipc_dev->dev);
+
+	ret = device_add(&ipc_dev->dev);
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev, "%s device create failed\n",
+				__func__);
+		ret = -ENODEV;
+		goto err_dev_add;
+	}
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (devm_request_irq(&ipc_dev->dev,
+				ipc_dev->cfg->irq,
+				ipc_dev_irq_handler,
+				ipc_dev->cfg->irqflags,
+				dev_name(&ipc_dev->dev),
+				ipc_dev)) {
+			dev_err(&ipc_dev->dev,
+					"Failed to request irq\n");
+			goto err_irq_request;
+		}
+	}
+
+	*ptr = ipc_dev;
+
+	devres_add(dev, ptr);
+
+	return ipc_dev;
+
+err_irq_request:
+	device_del(&ipc_dev->dev);
+err_dev_add:
+	kfree(ipc_dev);
+err_dev_create:
+	devres_free(ptr);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
+
+static int __init intel_ipc_init(void)
+{
+	ipc_channel_lock_init();
+	return class_register(&intel_ipc_class);
+}
+
+static void __exit intel_ipc_exit(void)
+{
+	class_unregister(&intel_ipc_class);
+}
+subsys_initcall(intel_ipc_init);
+module_exit(intel_ipc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kuppuswamy Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
+MODULE_DESCRIPTION("Intel IPC device class driver");
diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h b/include/linux/platform_data/x86/intel_ipc_dev.h
new file mode 100644
index 0000000..eaeedaf
--- /dev/null
+++ b/include/linux/platform_data/x86/intel_ipc_dev.h
@@ -0,0 +1,206 @@
+/*
+ * Intel IPC class device header file.
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#ifndef INTEL_IPC_DEV_H
+#define INTEL_IPC_DEV_H
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+/* IPC channel type */
+#define IPC_CHANNEL_IA_PMC                      0
+#define IPC_CHANNEL_IA_PUNIT                    1
+#define IPC_CHANNEL_PMC_PUNIT                   2
+#define IPC_CHANNEL_IA_SCU                      3
+#define IPC_CHANNEL_MAX                         4
+
+/* IPC return code */
+#define IPC_DEV_ERR_NONE			0
+#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
+#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
+#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
+#define IPC_DEV_ERR_CMD_INVALID			4
+#define IPC_DEV_ERR_CMD_FAILED			5
+#define IPC_DEV_ERR_EMSECURITY			6
+#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
+#define IPC_DEV_ERR_MAX				8
+
+/* IPC mode */
+#define IPC_DEV_MODE_IRQ			0
+#define IPC_DEV_MODE_POLLING			1
+
+/* IPC dev constants */
+#define IPC_DEV_CMD_LOOP_CNT			3000000
+#define IPC_DEV_CMD_TIMEOUT			3 * HZ
+#define IPC_DEV_DATA_BUFFER_SIZE		16
+
+struct intel_ipc_dev;
+struct intel_ipc_raw_cmd;
+
+/**
+ * struct intel_ipc_dev_cfg - IPC device config structure.
+ *
+ * IPC device drivers uses the following config options to
+ * register new IPC device.
+ *
+ * @cmd_regs            : IPC device command base regmap.
+ * @data_regs           : IPC device data base regmap.
+ * @wrbuf_reg           : IPC device data write register address.
+ * @rbuf_reg            : IPC device data read register address.
+ * @sptr_reg            : IPC device source data pointer register address.
+ * @dptr_reg            : IPC device destination data pointer register
+ *                        address.
+ * @status_reg          : IPC command status register address.
+ * @cmd_reg             : IPC command register address.
+ * @mode                : IRQ/POLLING mode.
+ * @irq                 : IPC device IRQ number.
+ * @irqflags            : IPC device IRQ flags.
+ * @chan_type           : IPC device channel type(PMC/PUNIT).
+ * @msi                 : Enable/Disable MSI for IPC commands.
+ * @support_dptr        : Support DPTR update.
+ * @support_sptr        : Support SPTR update.
+ *
+ */
+struct intel_ipc_dev_cfg {
+	struct regmap *cmd_regs;
+	struct regmap *data_regs;
+	unsigned int wrbuf_reg;
+	unsigned int rbuf_reg;
+	unsigned int sptr_reg;
+	unsigned int dptr_reg;
+	unsigned int status_reg;
+	unsigned int cmd_reg;
+	int mode;
+	int irq;
+	int irqflags;
+	int chan_type;
+	bool use_msi;
+	bool support_dptr;
+	bool support_sptr;
+};
+
+/**
+ * struct intel_ipc_dev_ops - IPC device ops structure.
+ *
+ * Call backs for IPC device specific operations.
+ *
+ * @to_err_code         : Status to error code conversion function.
+ * @busy_check          : Check for IPC busy status.
+ * @enable_msi          : Enable MSI for IPC commands.
+ * @pre_simple_cmd_fn   : Custom pre-processing function for
+ *                        ipc_dev_simple_cmd()
+ * @pre_cmd_fn          : Custom pre-processing function for
+ *                        ipc_dev_cmd()
+ * @pre_raw_cmd_fn      : Custom pre-processing function for
+ *                        ipc_dev_raw_cmd()
+ *
+ */
+struct intel_ipc_dev_ops {
+	int (*to_err_code)(int status);
+	int (*busy_check)(int status);
+	u32 (*enable_msi)(u32 cmd);
+	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
+	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
+			u32 *out, u32 outlen);
+	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+			u32 *out, u32 outlen, u32 dptr, u32 sptr);
+	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq);
+};
+
+/**
+ * struct intel_ipc_dev - Intel IPC device structure.
+ *
+ * Used with devm_intel_ipc_dev_create() to create new IPC device.
+ *
+ * @dev                 : IPC device object.
+ * @cmd                 : Current IPC device command.
+ * @cmd_complete        : Command completion object.
+ * @lock                : Lock to protect IPC device structure.
+ * @ops                 : IPC device ops pointer.
+ * @cfg                 : IPC device cfg pointer.
+ *
+ */
+struct intel_ipc_dev {
+	struct device dev;
+	int cmd;
+	struct completion cmd_complete;
+	struct mutex lock;
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+};
+
+#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
+
+/* API to create new IPC device */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops);
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen);
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen);
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name);
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	put_device(&ipc_dev->dev);
+}
+#else
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
+		struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
+		u32 *cmd_list, u32 cmdlen)
+{
+	return -EINVAL;
+}
+
+static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
+		u32 dptr, u32 sptr);
+{
+	return -EINVAL;
+}
+
+static inline struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+	return NULL;
+}
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+{
+	return NULL;
+}
+
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	return NULL;
+}
+#endif /* CONFIG_INTEL_IPC_DEV */
+#endif /* INTEL_IPC_DEV_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, x86-DgEjT+Ai2ygdnm+yROfE0A,
	wim-IQzOog9fTRqzQB+pC5nmwQ, mingo-H+wXaHxf7aLQT0dZR+AlfA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	qipeng.zha-ral2JQCrhuEAvxtiuMwx3w, hpa-YMNOUZJC4hwAvxtiuMwx3w,
	dvhart-wEGCiKHe2LqWVfeAwA7xHQ, tglx-hfZtesqFncYOwBW4kG4KsQ,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A, andy-wEGCiKHe2LqWVfeAwA7xHQ,
	souvik.k.chakravarty-ral2JQCrhuEAvxtiuMwx3w
  Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	platform-driver-x86-u79uwXL29TY76Z2rM5mHXA,
	sathyaosid-Re5JQEeQqe8AvxtiuMwx3w, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
redundantly implements the same IPC features and has lot of code
duplication between them. This driver addresses this issue by grouping
the common IPC functionalities under the same driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
 drivers/platform/x86/Kconfig                    |   8 +
 drivers/platform/x86/Makefile                   |   1 +
 drivers/platform/x86/intel_ipc_dev.c            | 576 ++++++++++++++++++++++++
 include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
 4 files changed, 791 insertions(+)
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
 create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h

Changes since v4:
 * None

Changes since v3:
 * Fixed NULL pointer exception in intel_ipc_dev_get().
 * Fixed error in check for duplicate intel_ipc_dev.
 * Added custom interrupt handler support.
 * Used char array for error string conversion.
 * Added put dev support.
 * Added devm_* variant of intel_ipc_dev_get().

Changes since v2:
 * Added ipc_dev_cmd API support.

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index da2d9ba..724ee696 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1153,6 +1153,14 @@ config SILEAD_DMI
 	  with the OS-image for the device. This option supplies the missing
 	  information. Enable this for x86 tablets with Silead touchscreens.
 
+config INTEL_IPC_DEV
+	bool "Intel IPC Device Driver"
+	depends on X86_64
+	---help---
+	  This driver implements core features of Intel IPC device. Devices
+	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
+	  driver to implement IPC protocol of their respective device.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2b315d0..99a1af1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+= pmc_atom.o
 obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
 obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
 obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
+obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
diff --git a/drivers/platform/x86/intel_ipc_dev.c b/drivers/platform/x86/intel_ipc_dev.c
new file mode 100644
index 0000000..f55ddec
--- /dev/null
+++ b/drivers/platform/x86/intel_ipc_dev.c
@@ -0,0 +1,576 @@
+/*
+ * intel_ipc_dev.c: Intel IPC device class driver
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+#include <linux/regmap.h>
+
+/* mutex to sync different ipc devices in same channel */
+static struct mutex channel_lock[IPC_CHANNEL_MAX];
+
+static char *ipc_err_sources[] = {
+	[IPC_DEV_ERR_NONE] =
+		"No error",
+	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
+		"Command not-supported/Invalid",
+	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
+		"Command not-serviced/Invalid param",
+	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
+		"Unable-to-service/Cmd-timeout",
+	[IPC_DEV_ERR_CMD_INVALID] =
+		"Command-invalid/Cmd-locked",
+	[IPC_DEV_ERR_CMD_FAILED] =
+		"Command-failed/Invalid-VR-id",
+	[IPC_DEV_ERR_EMSECURITY] =
+		"Invalid Battery/VR-Error",
+	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
+		"Unsigned kernel",
+};
+
+static void ipc_channel_lock_init(void)
+{
+	int i;
+
+	for (i = 0; i < IPC_CHANNEL_MAX; i++)
+		mutex_init(&channel_lock[i]);
+}
+
+static struct class intel_ipc_class = {
+	.name = "intel_ipc",
+	.owner = THIS_MODULE,
+};
+
+static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* acquire channel lock */
+	mutex_lock(&channel_lock[chan_type]);
+
+	/* acquire IPC device lock */
+	mutex_lock(&ipc_dev->lock);
+
+	return 0;
+}
+
+static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev)
+{
+	int chan_type;
+
+	if (!ipc_dev || !ipc_dev->cfg)
+		return -ENODEV;
+
+	chan_type = ipc_dev->cfg->chan_type;
+	if (chan_type > IPC_CHANNEL_MAX)
+		return -EINVAL;
+
+	/* release IPC device lock */
+	mutex_unlock(&ipc_dev->lock);
+
+	/* release channel lock */
+	mutex_unlock(&channel_lock[chan_type]);
+
+	return 0;
+}
+
+static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
+	int error)
+{
+	if (error < IPC_DEV_ERR_MAX)
+		return ipc_err_sources[error];
+
+	return "Unknown Command";
+}
+
+/* Helper function to send given command to IPC device */
+static inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)
+{
+	ipc_dev->cmd = cmd;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
+		reinit_completion(&ipc_dev->cmd_complete);
+
+	if (ipc_dev->ops->enable_msi)
+		cmd = ipc_dev->ops->enable_msi(cmd);
+
+	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
+}
+
+static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev)
+{
+	int status;
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->busy_check)
+		return ipc_dev->ops->busy_check(status);
+
+	return 0;
+}
+
+/* Check the status of IPC command and return err code if failed */
+static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev)
+{
+	int loop_count = IPC_DEV_CMD_LOOP_CNT;
+	int status;
+	int ret = 0;
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
+				IPC_DEV_CMD_TIMEOUT))
+			ret = -ETIMEDOUT;
+	} else {
+		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
+			udelay(1);
+		if (!loop_count)
+			ret = -ETIMEDOUT;
+	}
+
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev,
+				"IPC timed out, CMD=0x%x\n", ipc_dev->cmd);
+		return ret;
+	}
+
+	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);
+
+	if (ipc_dev->ops->to_err_code)
+		ret = ipc_dev->ops->to_err_code(status);
+
+	if (ret) {
+		dev_err(&ipc_dev->dev,
+				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
+				ipc_dev_err_string(ipc_dev, ret),
+				status, ipc_dev->cmd);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * ipc_dev_simple_cmd() - Send simple IPC command
+ * @ipc_dev     : Reference to ipc device.
+ * @cmd_list    : IPC command list.
+ * @cmdlen      : Number of cmd/sub-cmds.
+ *
+ * Send a simple IPC command to ipc device.
+ * Use this when don't need to specify input/output data
+ * and source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen)
+{
+	int ret;
+
+	if (!cmd_list)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler */
+	if (ipc_dev->ops->pre_simple_cmd_fn) {
+		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
+
+/**
+ * ipc_dev_cmd() - Send IPC command with data.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in dwords.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ *
+ * Send an IPC command to device with input/output data.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	int ret;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_cmd_fn) {
+		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, in, inlen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_cmd);
+
+/**
+ * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
+ * @ipc_dev     : Reference to ipc_dev.
+ * @cmd_list    : Array of commands/sub-commands.
+ * @cmdlen      : Number of commands.
+ * @in          : Input data of this IPC command.
+ * @inlen       : Input data length in bytes.
+ * @out         : Output data of this IPC command.
+ * @outlen      : Length of output data in dwords.
+ * @dptr        : IPC destination data address.
+ * @sptr        : IPC source data address.
+ *
+ * Send an IPC command to device with input/output data and
+ * source/dest pointers.
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	int ret;
+	int inbuflen = DIV_ROUND_UP(inlen, 4);
+	u32 *inbuf;
+
+	if (!cmd_list || !in)
+		return -EINVAL;
+
+	inbuf = kzalloc(inbuflen, GFP_KERNEL);
+	if (!inbuf)
+		return -ENOMEM;
+
+	ret = ipc_dev_lock(ipc_dev);
+	if (ret)
+		return ret;
+
+	/* Call custom pre-processing handler. */
+	if (ipc_dev->ops->pre_raw_cmd_fn) {
+		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in, inlen,
+				out, outlen, dptr, sptr);
+		if (ret)
+			goto unlock_device;
+	}
+
+	/* If supported, write DPTR register.*/
+	if (ipc_dev->cfg->support_dptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->dptr_reg,
+				dptr);
+
+	/* If supported, write SPTR register. */
+	if (ipc_dev->cfg->support_sptr)
+		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->sptr_reg,
+				sptr);
+
+	memcpy(inbuf, in, inlen);
+
+	/* Write inlen dwords of data to wrbuf_reg. */
+	if (inlen > 0)
+		regmap_bulk_write(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
+
+	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
+
+	ret = ipc_dev_check_status(ipc_dev);
+
+	/* Read outlen dwords of data from rbug_reg. */
+	if (!ret && outlen > 0)
+		regmap_bulk_read(ipc_dev->cfg->data_regs,
+				ipc_dev->cfg->rbuf_reg, out, outlen);
+unlock_device:
+	ipc_dev_unlock(ipc_dev);
+	kfree(inbuf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
+
+/* sysfs option to send simple IPC commands from userspace */
+static ssize_t ipc_dev_cmd_reg_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
+	u32 cmd;
+	int ret;
+
+	ret = sscanf(buf, "%d", &cmd);
+	if (ret != 1) {
+		dev_err(dev, "Error args\n");
+		return -EINVAL;
+	}
+
+	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
+	if (ret) {
+		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
+		return ret;
+	}
+	return (ssize_t)count;
+}
+
+static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
+
+static struct attribute *ipc_dev_attrs[] = {
+	&dev_attr_send_cmd.attr,
+	NULL
+};
+
+static const struct attribute_group ipc_dev_group = {
+	.attrs = ipc_dev_attrs,
+};
+
+static const struct attribute_group *ipc_dev_groups[] = {
+	&ipc_dev_group,
+	NULL,
+};
+
+/* IPC device IRQ handler */
+static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id)
+{
+	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
+
+	if (ipc_dev->ops->pre_irq_handler_fn)
+		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
+
+	complete(&ipc_dev->cmd_complete);
+
+	return IRQ_HANDLED;
+}
+
+static void devm_intel_ipc_dev_release(struct device *dev, void *res)
+{
+	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
+
+	if (!ipc_dev)
+		return;
+
+	device_del(&ipc_dev->dev);
+
+	kfree(ipc_dev);
+}
+
+static int match_name(struct device *dev, const void *data)
+{
+        if (!dev_name(dev))
+                return 0;
+
+        return !strcmp(dev_name(dev), (char *)data);
+}
+
+/**
+ * intel_ipc_dev_get() - Get Intel IPC device from name.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+        struct device *dev;
+
+	if (!dev_name)
+		return ERR_PTR(-EINVAL);
+
+	dev = class_find_device(&intel_ipc_class, NULL, dev_name, match_name);
+
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
+
+static void devm_intel_ipc_dev_put(struct device *dev, void *res)
+{
+	intel_ipc_dev_put(*(struct intel_ipc_dev **)res);
+}
+
+/**
+ * devm_intel_ipc_dev_get() - Resource managed version of intel_ipc_dev_get().
+ * @dev         : Device pointer.
+ * @dev_name    : Name of the IPC device.
+ *
+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+
+	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = intel_ipc_dev_get(dev_name);
+	if (!IS_ERR_OR_NULL(ipc_dev)) {
+		*ptr = ipc_dev;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ipc_dev;
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
+
+/**
+ * devm_intel_ipc_dev_create() - Create IPC device
+ * @dev		: IPC parent device.
+ * @devname	: Name of the IPC device.
+ * @cfg		: IPC device configuration.
+ * @ops		: IPC device ops.
+ *
+ * Resource managed API to create IPC device with
+ * given configuration.
+ *
+ * Return	: IPC device pointer or ERR_PTR(error code).
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname,
+		struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	struct intel_ipc_dev **ptr, *ipc_dev;
+	int ret;
+
+	if (!dev && !devname && !cfg)
+		return ERR_PTR(-EINVAL);
+
+	if (intel_ipc_dev_get(devname)) {
+		dev_err(dev, "IPC device %s already exist\n", devname);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
+			GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
+	if (!ipc_dev) {
+		ret = -ENOMEM;
+		goto err_dev_create;
+	}
+
+	ipc_dev->dev.class = &intel_ipc_class;
+	ipc_dev->dev.parent = dev;
+	ipc_dev->dev.groups = ipc_dev_groups;
+	ipc_dev->cfg = cfg;
+	ipc_dev->ops = ops;
+
+	mutex_init(&ipc_dev->lock);
+	init_completion(&ipc_dev->cmd_complete);
+	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
+	dev_set_name(&ipc_dev->dev, devname);
+	device_initialize(&ipc_dev->dev);
+
+	ret = device_add(&ipc_dev->dev);
+	if (ret < 0) {
+		dev_err(&ipc_dev->dev, "%s device create failed\n",
+				__func__);
+		ret = -ENODEV;
+		goto err_dev_add;
+	}
+
+	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+		if (devm_request_irq(&ipc_dev->dev,
+				ipc_dev->cfg->irq,
+				ipc_dev_irq_handler,
+				ipc_dev->cfg->irqflags,
+				dev_name(&ipc_dev->dev),
+				ipc_dev)) {
+			dev_err(&ipc_dev->dev,
+					"Failed to request irq\n");
+			goto err_irq_request;
+		}
+	}
+
+	*ptr = ipc_dev;
+
+	devres_add(dev, ptr);
+
+	return ipc_dev;
+
+err_irq_request:
+	device_del(&ipc_dev->dev);
+err_dev_add:
+	kfree(ipc_dev);
+err_dev_create:
+	devres_free(ptr);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
+
+static int __init intel_ipc_init(void)
+{
+	ipc_channel_lock_init();
+	return class_register(&intel_ipc_class);
+}
+
+static void __exit intel_ipc_exit(void)
+{
+	class_unregister(&intel_ipc_class);
+}
+subsys_initcall(intel_ipc_init);
+module_exit(intel_ipc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kuppuswamy Sathyanarayanan<sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>");
+MODULE_DESCRIPTION("Intel IPC device class driver");
diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h b/include/linux/platform_data/x86/intel_ipc_dev.h
new file mode 100644
index 0000000..eaeedaf
--- /dev/null
+++ b/include/linux/platform_data/x86/intel_ipc_dev.h
@@ -0,0 +1,206 @@
+/*
+ * Intel IPC class device header file.
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#ifndef INTEL_IPC_DEV_H
+#define INTEL_IPC_DEV_H
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+/* IPC channel type */
+#define IPC_CHANNEL_IA_PMC                      0
+#define IPC_CHANNEL_IA_PUNIT                    1
+#define IPC_CHANNEL_PMC_PUNIT                   2
+#define IPC_CHANNEL_IA_SCU                      3
+#define IPC_CHANNEL_MAX                         4
+
+/* IPC return code */
+#define IPC_DEV_ERR_NONE			0
+#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
+#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
+#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
+#define IPC_DEV_ERR_CMD_INVALID			4
+#define IPC_DEV_ERR_CMD_FAILED			5
+#define IPC_DEV_ERR_EMSECURITY			6
+#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
+#define IPC_DEV_ERR_MAX				8
+
+/* IPC mode */
+#define IPC_DEV_MODE_IRQ			0
+#define IPC_DEV_MODE_POLLING			1
+
+/* IPC dev constants */
+#define IPC_DEV_CMD_LOOP_CNT			3000000
+#define IPC_DEV_CMD_TIMEOUT			3 * HZ
+#define IPC_DEV_DATA_BUFFER_SIZE		16
+
+struct intel_ipc_dev;
+struct intel_ipc_raw_cmd;
+
+/**
+ * struct intel_ipc_dev_cfg - IPC device config structure.
+ *
+ * IPC device drivers uses the following config options to
+ * register new IPC device.
+ *
+ * @cmd_regs            : IPC device command base regmap.
+ * @data_regs           : IPC device data base regmap.
+ * @wrbuf_reg           : IPC device data write register address.
+ * @rbuf_reg            : IPC device data read register address.
+ * @sptr_reg            : IPC device source data pointer register address.
+ * @dptr_reg            : IPC device destination data pointer register
+ *                        address.
+ * @status_reg          : IPC command status register address.
+ * @cmd_reg             : IPC command register address.
+ * @mode                : IRQ/POLLING mode.
+ * @irq                 : IPC device IRQ number.
+ * @irqflags            : IPC device IRQ flags.
+ * @chan_type           : IPC device channel type(PMC/PUNIT).
+ * @msi                 : Enable/Disable MSI for IPC commands.
+ * @support_dptr        : Support DPTR update.
+ * @support_sptr        : Support SPTR update.
+ *
+ */
+struct intel_ipc_dev_cfg {
+	struct regmap *cmd_regs;
+	struct regmap *data_regs;
+	unsigned int wrbuf_reg;
+	unsigned int rbuf_reg;
+	unsigned int sptr_reg;
+	unsigned int dptr_reg;
+	unsigned int status_reg;
+	unsigned int cmd_reg;
+	int mode;
+	int irq;
+	int irqflags;
+	int chan_type;
+	bool use_msi;
+	bool support_dptr;
+	bool support_sptr;
+};
+
+/**
+ * struct intel_ipc_dev_ops - IPC device ops structure.
+ *
+ * Call backs for IPC device specific operations.
+ *
+ * @to_err_code         : Status to error code conversion function.
+ * @busy_check          : Check for IPC busy status.
+ * @enable_msi          : Enable MSI for IPC commands.
+ * @pre_simple_cmd_fn   : Custom pre-processing function for
+ *                        ipc_dev_simple_cmd()
+ * @pre_cmd_fn          : Custom pre-processing function for
+ *                        ipc_dev_cmd()
+ * @pre_raw_cmd_fn      : Custom pre-processing function for
+ *                        ipc_dev_raw_cmd()
+ *
+ */
+struct intel_ipc_dev_ops {
+	int (*to_err_code)(int status);
+	int (*busy_check)(int status);
+	u32 (*enable_msi)(u32 cmd);
+	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
+	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
+			u32 *out, u32 outlen);
+	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+			u32 *out, u32 outlen, u32 dptr, u32 sptr);
+	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq);
+};
+
+/**
+ * struct intel_ipc_dev - Intel IPC device structure.
+ *
+ * Used with devm_intel_ipc_dev_create() to create new IPC device.
+ *
+ * @dev                 : IPC device object.
+ * @cmd                 : Current IPC device command.
+ * @cmd_complete        : Command completion object.
+ * @lock                : Lock to protect IPC device structure.
+ * @ops                 : IPC device ops pointer.
+ * @cfg                 : IPC device cfg pointer.
+ *
+ */
+struct intel_ipc_dev {
+	struct device dev;
+	int cmd;
+	struct completion cmd_complete;
+	struct mutex lock;
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+};
+
+#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
+
+/* API to create new IPC device */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops);
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen);
+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u32 *in, u32 inlen, u32 *out, u32 outlen);
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name);
+struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	put_device(&ipc_dev->dev);
+}
+#else
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
+		struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
+		u32 *cmd_list, u32 cmdlen)
+{
+	return -EINVAL;
+}
+
+static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
+		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
+		u32 dptr, u32 sptr);
+{
+	return -EINVAL;
+}
+
+static inline struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)
+{
+	return NULL;
+}
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
+					const char *dev_name);
+{
+	return NULL;
+}
+
+static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev)
+{
+	return NULL;
+}
+#endif /* CONFIG_INTEL_IPC_DEV */
+#endif /* INTEL_IPC_DEV_H */
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 5/8] platform/x86: intel_punit_ipc: Fix resource ioremap warning
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

For PUNIT device, ISPDRIVER_IPC and GTDDRIVER_IPC resources are
not mandatory. So when PMC IPC driver creates a PUNIT device, if
these resources are not available then it creates dummy resource
entries for these missing resources. But during PUNIT device probe
, doing ioremap on these dummy resources generates following warning
messages.

intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]

This patch fixes this issue by adding extra check for resource size
before performing ioremap operation.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_punit_ipc.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Changes since v4:
 * None

diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
index a47a41f..b5b8901 100644
--- a/drivers/platform/x86/intel_punit_ipc.c
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev)
 	 * - GTDRIVER_IPC BASE_IFACE
 	 */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 5/8] platform/x86: intel_punit_ipc: Fix resource ioremap warning
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

For PUNIT device, ISPDRIVER_IPC and GTDDRIVER_IPC resources are
not mandatory. So when PMC IPC driver creates a PUNIT device, if
these resources are not available then it creates dummy resource
entries for these missing resources. But during PUNIT device probe
, doing ioremap on these dummy resources generates following warning
messages.

intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]
intel_punit_ipc intel_punit_ipc: can't request region for resource [mem
0x00000000]

This patch fixes this issue by adding extra check for resource size
before performing ioremap operation.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 drivers/platform/x86/intel_punit_ipc.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Changes since v4:
 * None

diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
index a47a41f..b5b8901 100644
--- a/drivers/platform/x86/intel_punit_ipc.c
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev)
 	 * - GTDRIVER_IPC BASE_IFACE
 	 */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
-	if (res) {
+	if (res && resource_size(res) > 1) {
 		addr = devm_ioremap_resource(&pdev->dev, res);
 		if (!IS_ERR(addr))
 			punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC
user drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC
driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
 drivers/platform/x86/Kconfig                  |   1 +
 drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
 drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
 4 files changed, 223 insertions(+), 303 deletions(-)

Changes since v4:
 * None

Changes since v2:
 * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
 * Added intel_ipc_dev_put() support.

Changes since v1:
 * Removed custom APIs.
 * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
   IPC driver.

diff --git a/arch/x86/include/asm/intel_punit_ipc.h b/arch/x86/include/asm/intel_punit_ipc.h
index 201eb9d..cf1630c 100644
--- a/arch/x86/include/asm/intel_punit_ipc.h
+++ b/arch/x86/include/asm/intel_punit_ipc.h
@@ -1,10 +1,8 @@
 #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
 #define  _ASM_X86_INTEL_PUNIT_IPC_H_
 
-/*
- * Three types of 8bit P-Unit IPC commands are supported,
- * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
- */
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+
 typedef enum {
 	BIOS_IPC = 0,
 	GTDRIVER_IPC,
@@ -12,61 +10,60 @@ typedef enum {
 	RESERVED_IPC,
 } IPC_TYPE;
 
-#define IPC_TYPE_OFFSET			6
-#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC << IPC_TYPE_OFFSET)
+#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
+#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
+#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
+#define PUNIT_PARAM_LEN				3
 
 /* BIOS => Pcode commands */
-#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE | 0x00)
-#define IPC_PUNIT_BIOS_VR_INTERFACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
-#define IPC_PUNIT_BIOS_READ_PCS			(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
-#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE | 0x03)
-#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
-#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
-#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
-#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE | 0x07)
-#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
-#define IPC_PUNIT_BIOS_READ_TELE_INFO		(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
-#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
-#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
-#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
-#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
-#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
-#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
-#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
-#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
-#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
-#define IPC_PUNIT_BIOS_RESERVED			(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
-#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
-#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
-#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
-#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
-#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
-#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
-#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
-#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
+#define IPC_PUNIT_BIOS_ZERO			(0x00)
+#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
+#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
+#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
+#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
+#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
+#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
+#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
+#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
+#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
+#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
+#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
+#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
+#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
+#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
+#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
+#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
+#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
+#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
+#define IPC_PUNIT_BIOS_RESERVED			(0x13)
+#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
+#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
+#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
+#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
+#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
+#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
+#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
+#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
 
 /* GT Driver => Pcode commands */
-#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE | 0x00)
-#define IPC_PUNIT_GTD_CONFIG			(IPC_PUNIT_GTD_CMD_BASE | 0x01)
-#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
-#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
-#define IPC_PUNIT_GTD_GET_WM_VAL		(IPC_PUNIT_GTD_CMD_BASE | 0x06)
-#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
-#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
-#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
-#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
-#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
+#define IPC_PUNIT_GTD_ZERO			(0x00)
+#define IPC_PUNIT_GTD_CONFIG			(0x01)
+#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
+#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
+#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
+#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
+#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
+#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
+#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
+#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
 
 /* ISP Driver => Pcode commands */
-#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE | 0x00)
-#define IPC_PUNIT_ISPD_CONFIG			(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
-#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
-#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
-#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
-#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
+#define IPC_PUNIT_ISPD_ZERO			(0x00)
+#define IPC_PUNIT_ISPD_CONFIG			(0x01)
+#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
+#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
+#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
+#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
 
 /* Error codes */
 #define IPC_PUNIT_ERR_SUCCESS			0
@@ -77,25 +74,11 @@ typedef enum {
 #define IPC_PUNIT_ERR_INVALID_VR_ID		5
 #define IPC_PUNIT_ERR_VR_ERR			6
 
-#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
-
-int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
-int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
-
-#else
-
-static inline int intel_punit_ipc_simple_command(int cmd,
-						  int para1, int para2)
+static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32 param3)
 {
-	return -ENODEV;
+	cmd[0] = param1;
+	cmd[1] = param2;
+	cmd[2] = param3;
 }
 
-static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
-					  u32 *in, u32 *out)
-{
-	return -ENODEV;
-}
-
-#endif /* CONFIG_INTEL_PUNIT_IPC */
-
 #endif
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 724ee696..9442c23 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
 
 config INTEL_PUNIT_IPC
 	tristate "Intel P-Unit IPC Driver"
+	select REGMAP_MMIO
 	---help---
 	  This driver provides support for Intel P-Unit Mailbox IPC mechanism,
 	  which is used to bridge the communications between kernel and P-Unit.
diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
index b5b8901..f310a05 100644
--- a/drivers/platform/x86/intel_punit_ipc.c
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -18,18 +18,18 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+#include <linux/regmap.h>
 #include <asm/intel_punit_ipc.h>
 
-/* IPC Mailbox registers */
-#define OFFSET_DATA_LOW		0x0
-#define OFFSET_DATA_HIGH	0x4
 /* bit field of interface register */
 #define	CMD_RUN			BIT(31)
-#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
+#define CMD_ERRCODE_MASK	GENMASK(7, 0)
 #define	CMD_PARA1_SHIFT		8
 #define	CMD_PARA2_SHIFT		16
 
-#define CMD_TIMEOUT_SECONDS	1
+/* IPC PUNIT commands */
+#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7, 0)
 
 enum {
 	BASE_DATA = 0,
@@ -39,187 +39,42 @@ enum {
 
 typedef struct {
 	struct device *dev;
-	struct mutex lock;
-	int irq;
-	struct completion cmd_complete;
 	/* base of interface and data registers */
 	void __iomem *base[RESERVED_IPC][BASE_MAX];
+	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
 	IPC_TYPE type;
 } IPC_DEV;
 
 static IPC_DEV *punit_ipcdev;
 
-static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd)
-{
-	writel(cmd, ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
-
-static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
+const char *ipc_dev_name[RESERVED_IPC] = {
+	PUNIT_BIOS_IPC_DEV,
+	PUNIT_GTD_IPC_DEV,
+	PUNIT_ISP_IPC_DEV
+};
 
-static const char *ipc_err_string(int error)
-{
-	if (error == IPC_PUNIT_ERR_SUCCESS)
-		return "no error";
-	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
-		return "invalid command";
-	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
-		return "invalid parameter";
-	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
-		return "command timeout";
-	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
-		return "command locked";
-	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
-		return "invalid vr id";
-	else if (error == IPC_PUNIT_ERR_VR_ERR)
-		return "vr error";
-	else
-		return "unknown error";
-}
+static struct regmap_config punit_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+};
 
-static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
+int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
-	int errcode;
-	int status;
-
-	if (ipcdev->irq) {
-		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
-						 CMD_TIMEOUT_SECONDS * HZ)) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	} else {
-		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --loops)
-			udelay(1);
-		if (!loops) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	}
+	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
+		return -EINVAL;
 
-	status = ipc_read_status(ipcdev, type);
-	errcode = status & CMD_ERRCODE_MASK;
-	if (errcode) {
-		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
-			ipc_err_string(errcode), status);
-		return -EIO;
-	}
+	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
+		cmd_list[2] << CMD_PARA1_SHIFT;
 
 	return 0;
 }
 
-/**
- * intel_punit_ipc_simple_command() - Simple IPC command
- * @cmd:	IPC command code.
- * @para1:	First 8bit parameter, set 0 if not used.
- * @para2:	Second 8bit parameter, set 0 if not used.
- *
- * Send a IPC command to P-Unit when there is no data transaction
- *
- * Return:	IPC error code or 0 on success.
- */
-int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
-{
-	IPC_DEV *ipcdev = punit_ipcdev;
-	IPC_TYPE type;
-	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
-
-	reinit_completion(&ipcdev->cmd_complete);
-	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
-	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
-	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-	ret = intel_punit_ipc_check_status(ipcdev, type);
-
-	mutex_unlock(&ipcdev->lock);
-
-	return ret;
-}
-EXPORT_SYMBOL(intel_punit_ipc_simple_command);
-
-/**
- * intel_punit_ipc_command() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @para1:	First 8bit parameter, set 0 if not used.
- * @para2:	Second 8bit parameter, set 0 if not used.
- * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
- * @out:	Output data.
- *
- * Send a IPC command to P-Unit with data transaction
- *
- * Return:	IPC error code or 0 on success.
- */
-int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
-{
-	IPC_DEV *ipcdev = punit_ipcdev;
-	IPC_TYPE type;
-	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
-
-	reinit_completion(&ipcdev->cmd_complete);
-	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
-	if (in) {
-		ipc_write_data_low(ipcdev, type, *in);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			ipc_write_data_high(ipcdev, type, *++in);
-	}
-
-	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
-	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-
-	ret = intel_punit_ipc_check_status(ipcdev, type);
-	if (ret)
-		goto out;
-
-	if (out) {
-		*out = ipc_read_data_low(ipcdev, type);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			*++out = ipc_read_data_high(ipcdev, type);
-	}
-
-out:
-	mutex_unlock(&ipcdev->lock);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
-
-static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
+/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */
+int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
+		u32 outlen, u32 dptr, u32 sptr)
 {
-	IPC_DEV *ipcdev = dev_id;
-
-	complete(&ipcdev->cmd_complete);
-	return IRQ_HANDLED;
+	return pre_simple_cmd_fn(cmd_list, cmdlen);
 }
 
 static int intel_punit_get_bars(struct platform_device *pdev)
@@ -282,9 +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
 	return 0;
 }
 
+static int punit_ipc_err_code(int status)
+{
+	return (status & CMD_ERRCODE_MASK);
+}
+
+static int punit_ipc_busy_check(int status)
+{
+	return status | CMD_RUN;
+}
+
+static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
+		const char *devname,
+		int irq,
+		void __iomem *base,
+		void __iomem *data)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *cmd_regs, *data_regs;
+
+        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+        if (!cfg)
+                return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
+						  devname, "base");
+
+	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
+			&punit_regmap_config);
+	if (IS_ERR(cmd_regs)) {
+                dev_err(dev, "cmd_regs regmap init failed\n");
+                return ERR_CAST(cmd_regs);;
+        }
+
+	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
+						  devname, "data");
+
+        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
+			&punit_regmap_config);
+        if (IS_ERR(data_regs)) {
+                dev_err(dev, "data_regs regmap init failed\n");
+                return ERR_CAST(data_regs);;
+        }
+
+	/* set IPC dev ops */
+	ops->to_err_code = punit_ipc_err_code;
+	ops->busy_check = punit_ipc_busy_check;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+
+	if (irq > 0)
+	        cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+	        cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
+	cfg->irq = irq;
+	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
+	cfg->cmd_regs = cmd_regs;
+	cfg->data_regs = data_regs;
+
+	return devm_intel_ipc_dev_create(dev, devname, cfg, ops);
+}
+
 static int intel_punit_ipc_probe(struct platform_device *pdev)
 {
-	int irq, ret;
+	int irq, ret, i;
 
 	punit_ipcdev = devm_kzalloc(&pdev->dev,
 				    sizeof(*punit_ipcdev), GFP_KERNEL);
@@ -294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, punit_ipcdev);
 
 	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
-		punit_ipcdev->irq = 0;
-		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
-	} else {
-		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
-				       IRQF_NO_SUSPEND, "intel_punit_ipc",
-				       &punit_ipcdev);
-		if (ret) {
-			dev_err(&pdev->dev, "Failed to request irq: %d\n", irq);
-			return ret;
-		}
-		punit_ipcdev->irq = irq;
-	}
 
 	ret = intel_punit_get_bars(pdev);
 	if (ret)
-		goto out;
+		return ret;
+
+	for (i = 0; i < RESERVED_IPC; i++) {
+		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
+				&pdev->dev,
+				ipc_dev_name[i],
+				irq,
+				punit_ipcdev->base[i][BASE_IFACE],
+				punit_ipcdev->base[i][BASE_DATA]);
+
+		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
+			dev_err(&pdev->dev, "%s create failed\n",
+					ipc_dev_name[i]);
+			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
+		}
+	}
 
 	punit_ipcdev->dev = &pdev->dev;
-	mutex_init(&punit_ipcdev->lock);
-	init_completion(&punit_ipcdev->cmd_complete);
 
-out:
 	return ret;
-}
 
-static int intel_punit_ipc_remove(struct platform_device *pdev)
-{
-	return 0;
 }
 
 static const struct acpi_device_id punit_ipc_acpi_ids[] = {
@@ -332,7 +250,6 @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
 
 static struct platform_driver intel_punit_ipc_driver = {
 	.probe = intel_punit_ipc_probe,
-	.remove = intel_punit_ipc_remove,
 	.driver = {
 		.name = "intel_punit_ipc",
 		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids),
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index e0424d5..bf8284a 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -98,6 +98,7 @@ struct telem_ssram_region {
 };
 
 static struct telemetry_plt_config *telm_conf;
+static struct intel_ipc_dev *punit_bios_ipc_dev;
 
 /*
  * The following counters are programmed by default during setup.
@@ -127,7 +128,6 @@ static struct telemetry_evtmap
 	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
 };
 
-
 static struct telemetry_evtmap
 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVENTS] = {
 	{"IA_CORE0_C6_RES",			0x0400},
@@ -283,13 +283,12 @@ static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 {
 	u32 write_buf;
-	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
-				      index, 0, &write_buf, NULL);
-
-	return ret;
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
+	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
 
 static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
@@ -435,6 +434,7 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 	int ret, index, idx;
 	u32 *pss_evtmap;
 	u32 telem_ctrl;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	num_pss_evts = evtconfig.num_evts;
 	pss_period = evtconfig.period;
@@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* PSS Config */
 	/* Get telemetry EVENT CTL */
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
-				      0, 0, NULL, &telem_ctrl);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN, NULL,
+			0, &telem_ctrl, 1, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Read Failed\n");
 		return ret;
@@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* Disable Telemetry */
 	TELEM_DISABLE(telem_ctrl);
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				      0, 0, &telem_ctrl, NULL);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 		return ret;
@@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 	TELEM_ENABLE_PERIODIC(telem_ctrl);
 	telem_ctrl |= pss_period;
 
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				      0, 0, &telem_ctrl, NULL);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
 		return ret;
@@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device *pdev)
 {
 	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
 	u32 read_buf, events, event_regs;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 	int ret;
 
 	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
@@ -626,8 +632,9 @@ static int telemetry_setup(struct platform_device *pdev)
 	telm_conf->ioss_config.max_period = TELEM_MAX_PERIOD(read_buf);
 
 	/* PUNIT Mailbox Setup */
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0,
-				      NULL, &read_buf);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			NULL, 0, &read_buf, 1, 0, 0);
 	if (ret) {
 		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
 		return ret;
@@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 {
 	u32 telem_ctrl = 0;
 	int ret = 0;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	mutex_lock(&(telm_conf->telem_lock));
 	if (ioss_period) {
@@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		}
 
 		/* Get telemetry EVENT CTL */
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
-				0, 0, NULL, &telem_ctrl);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &telem_ctrl, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Read Failed\n");
 			goto out;
@@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
 		/* Disable Telemetry */
 		TELEM_DISABLE(telem_ctrl);
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
+				0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			goto out;
@@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		TELEM_ENABLE_PERIODIC(telem_ctrl);
 		telem_ctrl |= pss_period;
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
+				0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
 			goto out;
@@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 {
 	u32 temp = 0;
 	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	if (verbosity == NULL)
 		return -EINVAL;
@@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 	mutex_lock(&(telm_conf->telem_trace_lock));
 	switch (telem_unit) {
 	case TELEM_PSS:
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
-				0, 0, NULL, &temp);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &temp, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Read Failed\n");
 			goto out;
@@ -1058,15 +1071,16 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 {
 	u32 temp = 0;
 	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	verbosity &= TELEM_TRC_VERBOSITY_MASK;
 
 	mutex_lock(&(telm_conf->telem_trace_lock));
 	switch (telem_unit) {
 	case TELEM_PSS:
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
-				0, 0, NULL, &temp);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &temp, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Read Failed\n");
 			goto out;
@@ -1074,10 +1088,10 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 
 		TELEM_CLEAR_VERBOSITY_BITS(temp);
 		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
-
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
-				0, 0, &temp, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
+				0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
 			goto out;
@@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 	if (!id)
 		return -ENODEV;
 
+	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
+	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
+		return PTR_ERR(punit_bios_ipc_dev);
+
 	telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
 	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 static int telemetry_pltdrv_remove(struct platform_device *pdev)
 {
 	telemetry_clear_pltdata();
+	intel_ipc_dev_put(punit_bios_ipc_dev);
 	iounmap(telm_conf->pss_config.regmap);
 	iounmap(telm_conf->ioss_config.regmap);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC
user drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC
driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
 drivers/platform/x86/Kconfig                  |   1 +
 drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
 drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
 4 files changed, 223 insertions(+), 303 deletions(-)

Changes since v4:
 * None

Changes since v2:
 * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
 * Added intel_ipc_dev_put() support.

Changes since v1:
 * Removed custom APIs.
 * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
   IPC driver.

diff --git a/arch/x86/include/asm/intel_punit_ipc.h b/arch/x86/include/asm/intel_punit_ipc.h
index 201eb9d..cf1630c 100644
--- a/arch/x86/include/asm/intel_punit_ipc.h
+++ b/arch/x86/include/asm/intel_punit_ipc.h
@@ -1,10 +1,8 @@
 #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
 #define  _ASM_X86_INTEL_PUNIT_IPC_H_
 
-/*
- * Three types of 8bit P-Unit IPC commands are supported,
- * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
- */
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+
 typedef enum {
 	BIOS_IPC = 0,
 	GTDRIVER_IPC,
@@ -12,61 +10,60 @@ typedef enum {
 	RESERVED_IPC,
 } IPC_TYPE;
 
-#define IPC_TYPE_OFFSET			6
-#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC << IPC_TYPE_OFFSET)
-#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC << IPC_TYPE_OFFSET)
+#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
+#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
+#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
+#define PUNIT_PARAM_LEN				3
 
 /* BIOS => Pcode commands */
-#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE | 0x00)
-#define IPC_PUNIT_BIOS_VR_INTERFACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
-#define IPC_PUNIT_BIOS_READ_PCS			(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
-#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE | 0x03)
-#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
-#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
-#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
-#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE | 0x07)
-#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
-#define IPC_PUNIT_BIOS_READ_TELE_INFO		(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
-#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
-#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
-#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
-#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
-#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
-#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
-#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
-#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
-#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
-#define IPC_PUNIT_BIOS_RESERVED			(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
-#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
-#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
-#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
-#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
-#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
-#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
-#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
-#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
+#define IPC_PUNIT_BIOS_ZERO			(0x00)
+#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
+#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
+#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
+#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
+#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
+#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
+#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
+#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
+#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
+#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
+#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
+#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
+#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
+#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
+#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
+#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
+#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
+#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
+#define IPC_PUNIT_BIOS_RESERVED			(0x13)
+#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
+#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
+#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
+#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
+#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
+#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
+#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
+#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
 
 /* GT Driver => Pcode commands */
-#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE | 0x00)
-#define IPC_PUNIT_GTD_CONFIG			(IPC_PUNIT_GTD_CMD_BASE | 0x01)
-#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
-#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
-#define IPC_PUNIT_GTD_GET_WM_VAL		(IPC_PUNIT_GTD_CMD_BASE | 0x06)
-#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
-#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
-#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
-#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
-#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
+#define IPC_PUNIT_GTD_ZERO			(0x00)
+#define IPC_PUNIT_GTD_CONFIG			(0x01)
+#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
+#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
+#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
+#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
+#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
+#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
+#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
+#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
 
 /* ISP Driver => Pcode commands */
-#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE | 0x00)
-#define IPC_PUNIT_ISPD_CONFIG			(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
-#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
-#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
-#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
-#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
+#define IPC_PUNIT_ISPD_ZERO			(0x00)
+#define IPC_PUNIT_ISPD_CONFIG			(0x01)
+#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
+#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
+#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
+#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
 
 /* Error codes */
 #define IPC_PUNIT_ERR_SUCCESS			0
@@ -77,25 +74,11 @@ typedef enum {
 #define IPC_PUNIT_ERR_INVALID_VR_ID		5
 #define IPC_PUNIT_ERR_VR_ERR			6
 
-#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
-
-int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
-int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
-
-#else
-
-static inline int intel_punit_ipc_simple_command(int cmd,
-						  int para1, int para2)
+static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32 param3)
 {
-	return -ENODEV;
+	cmd[0] = param1;
+	cmd[1] = param2;
+	cmd[2] = param3;
 }
 
-static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
-					  u32 *in, u32 *out)
-{
-	return -ENODEV;
-}
-
-#endif /* CONFIG_INTEL_PUNIT_IPC */
-
 #endif
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 724ee696..9442c23 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
 
 config INTEL_PUNIT_IPC
 	tristate "Intel P-Unit IPC Driver"
+	select REGMAP_MMIO
 	---help---
 	  This driver provides support for Intel P-Unit Mailbox IPC mechanism,
 	  which is used to bridge the communications between kernel and P-Unit.
diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
index b5b8901..f310a05 100644
--- a/drivers/platform/x86/intel_punit_ipc.c
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -18,18 +18,18 @@
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+#include <linux/regmap.h>
 #include <asm/intel_punit_ipc.h>
 
-/* IPC Mailbox registers */
-#define OFFSET_DATA_LOW		0x0
-#define OFFSET_DATA_HIGH	0x4
 /* bit field of interface register */
 #define	CMD_RUN			BIT(31)
-#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
+#define CMD_ERRCODE_MASK	GENMASK(7, 0)
 #define	CMD_PARA1_SHIFT		8
 #define	CMD_PARA2_SHIFT		16
 
-#define CMD_TIMEOUT_SECONDS	1
+/* IPC PUNIT commands */
+#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7, 0)
 
 enum {
 	BASE_DATA = 0,
@@ -39,187 +39,42 @@ enum {
 
 typedef struct {
 	struct device *dev;
-	struct mutex lock;
-	int irq;
-	struct completion cmd_complete;
 	/* base of interface and data registers */
 	void __iomem *base[RESERVED_IPC][BASE_MAX];
+	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
 	IPC_TYPE type;
 } IPC_DEV;
 
 static IPC_DEV *punit_ipcdev;
 
-static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd)
-{
-	writel(cmd, ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
-
-static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
+const char *ipc_dev_name[RESERVED_IPC] = {
+	PUNIT_BIOS_IPC_DEV,
+	PUNIT_GTD_IPC_DEV,
+	PUNIT_ISP_IPC_DEV
+};
 
-static const char *ipc_err_string(int error)
-{
-	if (error == IPC_PUNIT_ERR_SUCCESS)
-		return "no error";
-	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
-		return "invalid command";
-	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
-		return "invalid parameter";
-	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
-		return "command timeout";
-	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
-		return "command locked";
-	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
-		return "invalid vr id";
-	else if (error == IPC_PUNIT_ERR_VR_ERR)
-		return "vr error";
-	else
-		return "unknown error";
-}
+static struct regmap_config punit_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+};
 
-static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
+int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
-	int errcode;
-	int status;
-
-	if (ipcdev->irq) {
-		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
-						 CMD_TIMEOUT_SECONDS * HZ)) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	} else {
-		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --loops)
-			udelay(1);
-		if (!loops) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	}
+	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
+		return -EINVAL;
 
-	status = ipc_read_status(ipcdev, type);
-	errcode = status & CMD_ERRCODE_MASK;
-	if (errcode) {
-		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
-			ipc_err_string(errcode), status);
-		return -EIO;
-	}
+	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
+		cmd_list[2] << CMD_PARA1_SHIFT;
 
 	return 0;
 }
 
-/**
- * intel_punit_ipc_simple_command() - Simple IPC command
- * @cmd:	IPC command code.
- * @para1:	First 8bit parameter, set 0 if not used.
- * @para2:	Second 8bit parameter, set 0 if not used.
- *
- * Send a IPC command to P-Unit when there is no data transaction
- *
- * Return:	IPC error code or 0 on success.
- */
-int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
-{
-	IPC_DEV *ipcdev = punit_ipcdev;
-	IPC_TYPE type;
-	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
-
-	reinit_completion(&ipcdev->cmd_complete);
-	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
-	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
-	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-	ret = intel_punit_ipc_check_status(ipcdev, type);
-
-	mutex_unlock(&ipcdev->lock);
-
-	return ret;
-}
-EXPORT_SYMBOL(intel_punit_ipc_simple_command);
-
-/**
- * intel_punit_ipc_command() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @para1:	First 8bit parameter, set 0 if not used.
- * @para2:	Second 8bit parameter, set 0 if not used.
- * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
- * @out:	Output data.
- *
- * Send a IPC command to P-Unit with data transaction
- *
- * Return:	IPC error code or 0 on success.
- */
-int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
-{
-	IPC_DEV *ipcdev = punit_ipcdev;
-	IPC_TYPE type;
-	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
-
-	reinit_completion(&ipcdev->cmd_complete);
-	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
-	if (in) {
-		ipc_write_data_low(ipcdev, type, *in);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			ipc_write_data_high(ipcdev, type, *++in);
-	}
-
-	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
-	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-
-	ret = intel_punit_ipc_check_status(ipcdev, type);
-	if (ret)
-		goto out;
-
-	if (out) {
-		*out = ipc_read_data_low(ipcdev, type);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			*++out = ipc_read_data_high(ipcdev, type);
-	}
-
-out:
-	mutex_unlock(&ipcdev->lock);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
-
-static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
+/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */
+int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
+		u32 outlen, u32 dptr, u32 sptr)
 {
-	IPC_DEV *ipcdev = dev_id;
-
-	complete(&ipcdev->cmd_complete);
-	return IRQ_HANDLED;
+	return pre_simple_cmd_fn(cmd_list, cmdlen);
 }
 
 static int intel_punit_get_bars(struct platform_device *pdev)
@@ -282,9 +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
 	return 0;
 }
 
+static int punit_ipc_err_code(int status)
+{
+	return (status & CMD_ERRCODE_MASK);
+}
+
+static int punit_ipc_busy_check(int status)
+{
+	return status | CMD_RUN;
+}
+
+static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
+		const char *devname,
+		int irq,
+		void __iomem *base,
+		void __iomem *data)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *cmd_regs, *data_regs;
+
+        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+        if (!cfg)
+                return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
+						  devname, "base");
+
+	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
+			&punit_regmap_config);
+	if (IS_ERR(cmd_regs)) {
+                dev_err(dev, "cmd_regs regmap init failed\n");
+                return ERR_CAST(cmd_regs);;
+        }
+
+	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
+						  devname, "data");
+
+        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
+			&punit_regmap_config);
+        if (IS_ERR(data_regs)) {
+                dev_err(dev, "data_regs regmap init failed\n");
+                return ERR_CAST(data_regs);;
+        }
+
+	/* set IPC dev ops */
+	ops->to_err_code = punit_ipc_err_code;
+	ops->busy_check = punit_ipc_busy_check;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+
+	if (irq > 0)
+	        cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+	        cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
+	cfg->irq = irq;
+	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
+	cfg->cmd_regs = cmd_regs;
+	cfg->data_regs = data_regs;
+
+	return devm_intel_ipc_dev_create(dev, devname, cfg, ops);
+}
+
 static int intel_punit_ipc_probe(struct platform_device *pdev)
 {
-	int irq, ret;
+	int irq, ret, i;
 
 	punit_ipcdev = devm_kzalloc(&pdev->dev,
 				    sizeof(*punit_ipcdev), GFP_KERNEL);
@@ -294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, punit_ipcdev);
 
 	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
-		punit_ipcdev->irq = 0;
-		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
-	} else {
-		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
-				       IRQF_NO_SUSPEND, "intel_punit_ipc",
-				       &punit_ipcdev);
-		if (ret) {
-			dev_err(&pdev->dev, "Failed to request irq: %d\n", irq);
-			return ret;
-		}
-		punit_ipcdev->irq = irq;
-	}
 
 	ret = intel_punit_get_bars(pdev);
 	if (ret)
-		goto out;
+		return ret;
+
+	for (i = 0; i < RESERVED_IPC; i++) {
+		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
+				&pdev->dev,
+				ipc_dev_name[i],
+				irq,
+				punit_ipcdev->base[i][BASE_IFACE],
+				punit_ipcdev->base[i][BASE_DATA]);
+
+		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
+			dev_err(&pdev->dev, "%s create failed\n",
+					ipc_dev_name[i]);
+			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
+		}
+	}
 
 	punit_ipcdev->dev = &pdev->dev;
-	mutex_init(&punit_ipcdev->lock);
-	init_completion(&punit_ipcdev->cmd_complete);
 
-out:
 	return ret;
-}
 
-static int intel_punit_ipc_remove(struct platform_device *pdev)
-{
-	return 0;
 }
 
 static const struct acpi_device_id punit_ipc_acpi_ids[] = {
@@ -332,7 +250,6 @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
 
 static struct platform_driver intel_punit_ipc_driver = {
 	.probe = intel_punit_ipc_probe,
-	.remove = intel_punit_ipc_remove,
 	.driver = {
 		.name = "intel_punit_ipc",
 		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids),
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index e0424d5..bf8284a 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -98,6 +98,7 @@ struct telem_ssram_region {
 };
 
 static struct telemetry_plt_config *telm_conf;
+static struct intel_ipc_dev *punit_bios_ipc_dev;
 
 /*
  * The following counters are programmed by default during setup.
@@ -127,7 +128,6 @@ static struct telemetry_evtmap
 	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
 };
 
-
 static struct telemetry_evtmap
 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVENTS] = {
 	{"IA_CORE0_C6_RES",			0x0400},
@@ -283,13 +283,12 @@ static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 {
 	u32 write_buf;
-	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
-				      index, 0, &write_buf, NULL);
-
-	return ret;
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
+	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
 
 static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
@@ -435,6 +434,7 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 	int ret, index, idx;
 	u32 *pss_evtmap;
 	u32 telem_ctrl;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	num_pss_evts = evtconfig.num_evts;
 	pss_period = evtconfig.period;
@@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* PSS Config */
 	/* Get telemetry EVENT CTL */
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
-				      0, 0, NULL, &telem_ctrl);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN, NULL,
+			0, &telem_ctrl, 1, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Read Failed\n");
 		return ret;
@@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* Disable Telemetry */
 	TELEM_DISABLE(telem_ctrl);
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				      0, 0, &telem_ctrl, NULL);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 		return ret;
@@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
 	TELEM_ENABLE_PERIODIC(telem_ctrl);
 	telem_ctrl |= pss_period;
 
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				      0, 0, &telem_ctrl, NULL);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
 		return ret;
@@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device *pdev)
 {
 	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
 	u32 read_buf, events, event_regs;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 	int ret;
 
 	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
@@ -626,8 +632,9 @@ static int telemetry_setup(struct platform_device *pdev)
 	telm_conf->ioss_config.max_period = TELEM_MAX_PERIOD(read_buf);
 
 	/* PUNIT Mailbox Setup */
-	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0,
-				      NULL, &read_buf);
+	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
+	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+			NULL, 0, &read_buf, 1, 0, 0);
 	if (ret) {
 		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
 		return ret;
@@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 {
 	u32 telem_ctrl = 0;
 	int ret = 0;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	mutex_lock(&(telm_conf->telem_lock));
 	if (ioss_period) {
@@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		}
 
 		/* Get telemetry EVENT CTL */
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
-				0, 0, NULL, &telem_ctrl);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &telem_ctrl, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Read Failed\n");
 			goto out;
@@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
 		/* Disable Telemetry */
 		TELEM_DISABLE(telem_ctrl);
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
+				0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
 			goto out;
@@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		TELEM_ENABLE_PERIODIC(telem_ctrl);
 		telem_ctrl |= pss_period;
 
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
-				0, 0, &telem_ctrl, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
+				0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
 			goto out;
@@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 {
 	u32 temp = 0;
 	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	if (verbosity == NULL)
 		return -EINVAL;
@@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 	mutex_lock(&(telm_conf->telem_trace_lock));
 	switch (telem_unit) {
 	case TELEM_PSS:
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
-				0, 0, NULL, &temp);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &temp, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Read Failed\n");
 			goto out;
@@ -1058,15 +1071,16 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 {
 	u32 temp = 0;
 	int ret;
+	u32 cmd[PUNIT_PARAM_LEN] = {0};
 
 	verbosity &= TELEM_TRC_VERBOSITY_MASK;
 
 	mutex_lock(&(telm_conf->telem_trace_lock));
 	switch (telem_unit) {
 	case TELEM_PSS:
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
-				0, 0, NULL, &temp);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				NULL, 0, &temp, 1, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Read Failed\n");
 			goto out;
@@ -1074,10 +1088,10 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 
 		TELEM_CLEAR_VERBOSITY_BITS(temp);
 		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
-
-		ret = intel_punit_ipc_command(
-				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
-				0, 0, &temp, NULL);
+		punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
+				0, 0);
+		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
+				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
 			goto out;
@@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 	if (!id)
 		return -ENODEV;
 
+	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
+	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
+		return PTR_ERR(punit_bios_ipc_dev);
+
 	telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
 	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 static int telemetry_pltdrv_remove(struct platform_device *pdev)
 {
 	telemetry_clear_pltdata();
+	intel_ipc_dev_put(punit_bios_ipc_dev);
 	iounmap(telm_conf->pss_config.regmap);
 	iounmap(telm_conf->ioss_config.regmap);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs. Also, cleaned up the driver to minimize
the usage of global variable ipcdev by propogating the struct
intel_pmc_ipc_dev pointer or by getting it from device private data.

This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 arch/x86/include/asm/intel_pmc_ipc.h          |  37 +--
 drivers/mfd/intel_soc_pmic_bxtwc.c            |  21 +-
 drivers/platform/x86/intel_pmc_ipc.c          | 393 ++++++++++----------------
 drivers/platform/x86/intel_telemetry_pltdrv.c | 119 ++++----
 include/linux/mfd/intel_soc_pmic.h            |   2 +
 5 files changed, 238 insertions(+), 334 deletions(-)

Changes since v4:
 * None

Changes since v3:
 * Added unique name to PMC regmaps.
 * Added support to clear interrupt bit.
 * Added intel_ipc_dev_put() support.

Changes since v1:
 * Removed custom APIs.
 * Cleaned up PMC IPC user drivers to use APIs provided by generic
   IPC driver.

diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index fac89eb..9fc7c3c 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -1,10 +1,15 @@
 #ifndef _ASM_X86_INTEL_PMC_IPC_H_
 #define  _ASM_X86_INTEL_PMC_IPC_H_
 
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+
+#define INTEL_PMC_IPC_DEV		"intel_pmc_ipc"
+#define PMC_PARAM_LEN			2
+
 /* Commands */
 #define PMC_IPC_PMIC_ACCESS		0xFF
-#define		PMC_IPC_PMIC_ACCESS_READ	0x0
-#define		PMC_IPC_PMIC_ACCESS_WRITE	0x1
+#define	PMC_IPC_PMIC_ACCESS_READ	0x0
+#define	PMC_IPC_PMIC_ACCESS_WRITE	0x1
 #define PMC_IPC_USB_PWR_CTRL		0xF0
 #define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
 #define PMC_IPC_PHY_CONFIG		0xEE
@@ -28,13 +33,14 @@
 #define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
 #define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
 
+static inline void pmc_cmd_init(u32 *cmd, u32 param1, u32 param2)
+{
+	cmd[0] = param1;
+	cmd[1] = param2;
+}
+
 #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
 
-int intel_pmc_ipc_simple_command(int cmd, int sub);
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen, u32 dptr, u32 sptr);
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen);
 int intel_pmc_s0ix_counter_read(u64 *data);
 int intel_pmc_gcr_read(u32 offset, u32 *data);
 int intel_pmc_gcr_write(u32 offset, u32 data);
@@ -42,23 +48,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
 
 #else
 
-static inline int intel_pmc_ipc_simple_command(int cmd, int sub)
-{
-	return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen, u32 dptr, u32 sptr)
-{
-	return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen)
-{
-	return -EINVAL;
-}
-
 static inline int intel_pmc_s0ix_counter_read(u64 *data)
 {
 	return -EINVAL;
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 15bc052..f9df9e7 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -271,6 +271,8 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 	u8 ipc_in[2];
 	u8 ipc_out[4];
 	struct intel_soc_pmic *pmic = context;
+	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+		PMC_IPC_PMIC_ACCESS_READ};
 
 	if (!pmic)
 		return -EINVAL;
@@ -284,9 +286,8 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 
 	ipc_in[0] = reg;
 	ipc_in[1] = i2c_addr;
-	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-			PMC_IPC_PMIC_ACCESS_READ,
-			ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
+	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN, ipc_in,
+			sizeof(ipc_in), (u32 *)ipc_out, 1, 0, 0);
 	if (ret) {
 		dev_err(pmic->dev, "Failed to read from PMIC\n");
 		return ret;
@@ -303,6 +304,8 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
 	int i2c_addr;
 	u8 ipc_in[3];
 	struct intel_soc_pmic *pmic = context;
+	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+		PMC_IPC_PMIC_ACCESS_WRITE};
 
 	if (!pmic)
 		return -EINVAL;
@@ -317,9 +320,8 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
 	ipc_in[0] = reg;
 	ipc_in[1] = i2c_addr;
 	ipc_in[2] = val;
-	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-			PMC_IPC_PMIC_ACCESS_WRITE,
-			ipc_in, sizeof(ipc_in), NULL, 0);
+	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN, ipc_in,
+			sizeof(ipc_in), NULL, 0, 0, 0);
 	if (ret) {
 		dev_err(pmic->dev, "Failed to write to PMIC\n");
 		return ret;
@@ -445,6 +447,10 @@ static int bxtwc_probe(struct platform_device *pdev)
 	if (!pmic)
 		return -ENOMEM;
 
+	pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+	if (IS_ERR_OR_NULL(pmic->ipc_dev))
+		return PTR_ERR(pmic->ipc_dev);
+
 	ret = platform_get_irq(pdev, 0);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Invalid IRQ\n");
@@ -562,7 +568,10 @@ static int bxtwc_probe(struct platform_device *pdev)
 
 static int bxtwc_remove(struct platform_device *pdev)
 {
+	struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
 	sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
+	intel_ipc_dev_put(pmic->ipc_dev);
 
 	return 0;
 }
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index fd5bcdf..35d4734 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -47,18 +47,8 @@
  * The ARC handles the interrupt and services it, writing optional data to
  * the IPC1 registers, updates the IPC_STS response register with the status.
  */
-#define IPC_CMD			0x0
-#define		IPC_CMD_MSI		0x100
 #define		IPC_CMD_SIZE		16
 #define		IPC_CMD_SUBCMD		12
-#define IPC_STATUS		0x04
-#define		IPC_STATUS_IRQ		0x4
-#define		IPC_STATUS_ERR		0x2
-#define		IPC_STATUS_BUSY		0x1
-#define IPC_SPTR		0x08
-#define IPC_DPTR		0x0C
-#define IPC_WRITE_BUFFER	0x80
-#define IPC_READ_BUFFER		0x90
 
 /* Residency with clock rate at 19.2MHz to usecs */
 #define S0IX_RESIDENCY_IN_USECS(d, s)		\
@@ -73,11 +63,6 @@
  */
 #define IPC_DATA_BUFFER_SIZE	16
 
-#define IPC_LOOP_CNT		3000000
-#define IPC_MAX_SEC		3
-
-#define IPC_TRIGGER_MODE_IRQ		true
-
 /* exported resources from IFWI */
 #define PLAT_RESOURCE_IPC_INDEX		0
 #define PLAT_RESOURCE_IPC_SIZE		0x1000
@@ -117,36 +102,41 @@
 #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
 #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+/* IPC PMC commands */
+#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
+#define	IPC_DEV_PMC_CMD_SIZE			16
+#define	IPC_DEV_PMC_CMD_SUBCMD			12
+#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK		GENMASK(7, 0)
+#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
+
+/*IPC PMC reg offsets */
+#define IPC_DEV_PMC_STATUS_OFFSET		0x04
+#define IPC_DEV_PMC_SPTR_OFFSET			0x08
+#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
+#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
+#define IPC_DEV_PMC_RBUF_OFFSET			0x90
+
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
+	struct intel_ipc_dev *pmc_ipc_dev;
+	struct intel_ipc_dev_ops ops;
+	struct intel_ipc_dev_cfg cfg;
 	void __iomem *ipc_base;
-	bool irq_mode;
-	int irq;
-	int cmd;
-	struct completion cmd_complete;
 
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	struct regmap *gcr_regs;
 } ipcdev;
 
-static char *ipc_err_sources[] = {
-	[IPC_ERR_NONE] =
-		"no error",
-	[IPC_ERR_CMD_NOT_SUPPORTED] =
-		"command not supported",
-	[IPC_ERR_CMD_NOT_SERVICED] =
-		"command not serviced",
-	[IPC_ERR_UNABLE_TO_SERVICE] =
-		"unable to service",
-	[IPC_ERR_CMD_INVALID] =
-		"command invalid",
-	[IPC_ERR_CMD_FAILED] =
-		"command failed",
-	[IPC_ERR_EMSECURITY] =
-		"Invalid Battery",
-	[IPC_ERR_UNSIGNEDKERNEL] =
-		"Unsigned kernel",
+static struct regmap_config pmc_regmap_config = {
+	.name = "intel_pmc_regs",
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
 };
 
 static struct regmap_config gcr_regmap_config = {
@@ -158,40 +148,6 @@ static struct regmap_config gcr_regmap_config = {
 	.max_register = PLAT_RESOURCE_GCR_SIZE,
 };
 
-/* Prevent concurrent calls to the PMC */
-static DEFINE_MUTEX(ipclock);
-
-static inline void ipc_send_command(u32 cmd)
-{
-	ipcdev.cmd = cmd;
-	if (ipcdev.irq_mode) {
-		reinit_completion(&ipcdev.cmd_complete);
-		cmd |= IPC_CMD_MSI;
-	}
-	writel(cmd, ipcdev.ipc_base + IPC_CMD);
-}
-
-static inline u32 ipc_read_status(void)
-{
-	return readl(ipcdev.ipc_base + IPC_STATUS);
-}
-
-static inline void ipc_data_writel(u32 data, u32 offset)
-{
-	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
-}
-
-static inline u8 __maybe_unused ipc_data_readb(u32 offset)
-{
-	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-static inline u32 ipc_data_readl(u32 offset)
-{
-	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-
 /**
  * intel_pmc_gcr_read() - Read PMC GCR register
  * @offset:	offset of GCR register from GCR address base
@@ -263,160 +219,109 @@ static int update_no_reboot_bit(void *priv, bool set)
 				    PMC_CFG_NO_REBOOT_MASK, value);
 }
 
-static int intel_pmc_ipc_check_status(void)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	int status;
-	int ret = 0;
-
-	if (ipcdev.irq_mode) {
-		if (0 == wait_for_completion_timeout(
-				&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
-			ret = -ETIMEDOUT;
-	} else {
-		int loop_count = IPC_LOOP_CNT;
-
-		while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
-			udelay(1);
-		if (loop_count == 0)
-			ret = -ETIMEDOUT;
-	}
-
-	status = ipc_read_status();
-	if (ret == -ETIMEDOUT) {
-		dev_err(ipcdev.dev,
-			"IPC timed out, TS=0x%x, CMD=0x%x\n",
-			status, ipcdev.cmd);
-		return ret;
-	}
+	if (!cmd_list || cmdlen != PMC_PARAM_LEN)
+		return -EINVAL;
 
-	if (status & IPC_STATUS_ERR) {
-		int i;
-
-		ret = -EIO;
-		i = (status >> IPC_CMD_SIZE) & 0xFF;
-		if (i < ARRAY_SIZE(ipc_err_sources))
-			dev_err(ipcdev.dev,
-				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
-				ipc_err_sources[i], status, ipcdev.cmd);
-		else
-			dev_err(ipcdev.dev,
-				"IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
-				status, ipcdev.cmd);
-		if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
-			ret = -EACCES;
-	}
+	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
 
-	return ret;
+	return 0;
 }
 
-/**
- * intel_pmc_ipc_simple_command() - Simple IPC command
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- *
- * Send a simple IPC command to PMC when don't need to specify
- * input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_simple_command(int cmd, int sub)
+static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+		u32 *out, u32 outlen, u32 dptr, u32 sptr)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
-	if (ipcdev.dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
-	ret = intel_pmc_ipc_check_status();
-	mutex_unlock(&ipclock);
+	if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE/4)
+		return -EINVAL;
 
-	return ret;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
 
-/**
- * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in bytes.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- * @sptr:	data writing to SPTR register.
- * @dptr:	data writing to DPTR register.
- *
- * Send an IPC command to PMC with input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
-			  u32 outlen, u32 dptr, u32 sptr)
-{
-	u32 wbuf[4] = { 0 };
-	int ret;
-	int i;
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
 
-	if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
-		return -EINVAL;
+	return 0;
+}
 
-	mutex_lock(&ipclock);
-	if (ipcdev.dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	memcpy(wbuf, in, inlen);
-	writel(dptr, ipcdev.ipc_base + IPC_DPTR);
-	writel(sptr, ipcdev.ipc_base + IPC_SPTR);
-	/* The input data register is 32bit register and inlen is in Byte */
-	for (i = 0; i < ((inlen + 3) / 4); i++)
-		ipc_data_writel(wbuf[i], 4 * i);
-	ipc_send_command((inlen << IPC_CMD_SIZE) |
-			(sub << IPC_CMD_SUBCMD) | cmd);
-	ret = intel_pmc_ipc_check_status();
-	if (!ret) {
-		/* out is read from 32bit register and outlen is in 32bit */
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(4 * i);
-	}
-	mutex_unlock(&ipclock);
+static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq)
+{
+	return regmap_write_bits(ipc_dev->cfg->cmd_regs,
+				  ipc_dev->cfg->status_reg,
+				  IPC_DEV_PMC_CMD_STATUS_IRQ,
+				  IPC_DEV_PMC_CMD_STATUS_IRQ);
+}
 
-	return ret;
+static int pmc_ipc_err_code(int status)
+{
+	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
+			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
 
-/**
- * intel_pmc_ipc_command() -  IPC command with input/output data
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in bytes.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- *
- * Send an IPC command to PMC with input/output data.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-			  u32 *out, u32 outlen)
+static int pmc_ipc_busy_check(int status)
 {
-	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
 
-static irqreturn_t ioc(int irq, void *dev_id)
+static u32 pmc_ipc_enable_msi(u32 cmd)
 {
-	int status;
+	return cmd | IPC_DEV_PMC_CMD_MSI;
+}
 
-	if (ipcdev.irq_mode) {
-		status = ipc_read_status();
-		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
-	}
-	complete(&ipcdev.cmd_complete);
+static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
+		struct device *pmc_dev,
+		void __iomem *base,
+		int irq)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *cmd_regs;
+
+	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+        cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
+			&pmc_regmap_config);
+        if (IS_ERR(cmd_regs)) {
+                dev_err(pmc_dev, "cmd_regs regmap init failed\n");
+                return ERR_CAST(cmd_regs);;
+        }
 
-	return IRQ_HANDLED;
+	/* set IPC dev ops */
+	ops->to_err_code = pmc_ipc_err_code;
+	ops->busy_check = pmc_ipc_busy_check;
+	ops->enable_msi = pmc_ipc_enable_msi;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+	ops->pre_irq_handler_fn = pre_irq_handler_fn;
+
+	/* set cfg options */
+	if (irq > 0)
+		cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+		cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PMC;
+	cfg->irq = irq;
+	cfg->use_msi = true;
+	cfg->support_sptr = true;
+	cfg->support_dptr = true;
+	cfg->cmd_regs = cmd_regs;
+	cfg->data_regs = cmd_regs;
+	cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
+	cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
+	cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
+	cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
+	cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
+
+	return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV, cfg, ops);
 }
 
 static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -428,8 +333,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (pmc->dev)
 		return -EBUSY;
 
-	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
-
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -438,15 +341,14 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (ret)
 		return ret;
 
-	init_completion(&pmc->cmd_complete);
-
 	pmc->ipc_base = pcim_iomap_table(pdev)[0];
 
-	ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
-				pmc);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		return ret;
+	pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			pmc->ipc_base, pdev->irq);
+	if (IS_ERR(pmc->pmc_ipc_dev)) {
+		dev_err(&pdev->dev,
+				"Failed to create PMC IPC device\n");
+		return PTR_ERR(pmc->pmc_ipc_dev);
 	}
 
 	pmc->dev = &pdev->dev;
@@ -474,19 +376,19 @@ static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
 					      struct device_attribute *attr,
 					      const char *buf, size_t count)
 {
-	int subcmd;
-	int cmd;
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
+	int cmd[2];
 	int ret;
 
-	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+	ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
 	if (ret != 2) {
 		dev_err(dev, "Error args\n");
 		return -EINVAL;
 	}
 
-	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
+	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
 	if (ret) {
-		dev_err(dev, "command %d error with %d\n", cmd, ret);
+		dev_err(dev, "command %d error with %d\n", cmd[0], ret);
 		return ret;
 	}
 	return (ssize_t)count;
@@ -496,22 +398,23 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
 					     struct device_attribute *attr,
 					     const char *buf, size_t count)
 {
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
 	unsigned long val;
-	int subcmd;
+	int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
 	int ret;
 
 	if (kstrtoul(buf, 0, &val))
 		return -EINVAL;
 
 	if (val)
-		subcmd = 1;
-	else
-		subcmd = 0;
-	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
+		cmd[1] = 1;
+
+	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
 	if (ret) {
-		dev_err(dev, "command north %d error with %d\n", subcmd, ret);
+		dev_err(dev, "command north %d error with %d\n", cmd[1], ret);
 		return ret;
 	}
+
 	return (ssize_t)count;
 }
 
@@ -689,6 +592,7 @@ static int ipc_create_pmc_devices(struct platform_device *pdev)
 
 static int ipc_plat_get_res(struct platform_device *pdev)
 {
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
 	struct resource *res;
 	void __iomem *addr;
 
@@ -707,9 +611,9 @@ static int ipc_plat_get_res(struct platform_device *pdev)
 	if (IS_ERR(addr))
 		return PTR_ERR(addr);
 
-	ipcdev.ipc_base = addr;
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
+	pmc->ipc_base = addr;
+	pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
+	dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
 
 	return 0;
 }
@@ -755,14 +659,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
 
 static int ipc_plat_probe(struct platform_device *pdev)
 {
-	int ret;
+	int ret, irq;
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
+
+	pmc->dev = &pdev->dev;
 
-	ipcdev.dev = &pdev->dev;
-	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
-	init_completion(&ipcdev.cmd_complete);
+	dev_set_drvdata(&pdev->dev, pmc);
 
-	ipcdev.irq = platform_get_irq(pdev, 0);
-	if (ipcdev.irq < 0) {
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
 		dev_err(&pdev->dev, "Failed to get irq\n");
 		return -EINVAL;
 	}
@@ -773,11 +678,11 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
-			ipcdev.gcr_mem_base, &gcr_regmap_config);
-        if (IS_ERR(ipcdev.gcr_regs)) {
-                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
-                return PTR_ERR(ipcdev.gcr_regs);;
+        pmc->gcr_regs = devm_regmap_init_mmio_clk(pmc->dev, NULL,
+			pmc->gcr_mem_base, &gcr_regmap_config);
+        if (IS_ERR(pmc->gcr_regs)) {
+                dev_err(&pdev->dev, "gcr_regs regmap init failed\n");
+                return PTR_ERR(pmc->gcr_regs);;
         }
 
 	ret = ipc_create_pmc_devices(pdev);
@@ -786,20 +691,20 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
-			     "intel_pmc_ipc", &ipcdev)) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		return -EBUSY;
-	}
-
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
 		return ret;
 	}
 
+	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			pmc->ipc_base, irq);
+	if (IS_ERR(pmc->pmc_ipc_dev)) {
+		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
+		return PTR_ERR(pmc->pmc_ipc_dev);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index bf8284a..a08a620 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -56,10 +56,6 @@
 #define IOSS_TELEM_TRACE_CTL_WRITE	0x6
 #define IOSS_TELEM_EVENT_CTL_READ	0x7
 #define IOSS_TELEM_EVENT_CTL_WRITE	0x8
-#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE	0x4
-#define IOSS_TELEM_READ_WORD		0x1
-#define IOSS_TELEM_WRITE_FOURBYTES	0x4
-#define IOSS_TELEM_EVT_WRITE_SIZE	0x3
 
 #define TELEM_INFO_SRAMEVTS_MASK	0xFF00
 #define TELEM_INFO_SRAMEVTS_SHIFT	0x8
@@ -98,7 +94,7 @@ struct telem_ssram_region {
 };
 
 static struct telemetry_plt_config *telm_conf;
-static struct intel_ipc_dev *punit_bios_ipc_dev;
+static struct intel_ipc_dev *punit_bios_ipc_dev, *pmc_ipc_dev;
 
 /*
  * The following counters are programmed by default during setup.
@@ -267,17 +263,16 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
 static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
 {
 	u32 write_buf;
-	int ret;
+	u32 cmd[PMC_PARAM_LEN] = {0};
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
 	write_buf <<= BITS_PER_BYTE;
 	write_buf |= index;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
-				    IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_WRITE);
 
-	return ret;
+	return ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
 
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -287,6 +282,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
 	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
+
 	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
 			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
@@ -298,15 +294,16 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	int ret, index, idx;
 	u32 *ioss_evtmap;
 	u32 telem_ctrl;
+	u32 cmd[PMC_PARAM_LEN] = {0};
 
 	num_ioss_evts = evtconfig.num_evts;
 	ioss_period = evtconfig.period;
 	ioss_evtmap = evtconfig.evtmap;
 
 	/* Get telemetry EVENT CTL */
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-				    &telem_ctrl, IOSS_TELEM_READ_WORD);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_READ);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+			&telem_ctrl, 1, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Read Failed\n");
 		return ret;
@@ -314,12 +311,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* Disable Telemetry */
 	TELEM_DISABLE(telem_ctrl);
-
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_WRITE,
-				    (u8 *)&telem_ctrl,
-				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-				    NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_WRITE);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 		return ret;
@@ -330,12 +324,11 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	if (action == TELEM_RESET) {
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl),
+				NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -360,11 +353,11 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -412,10 +405,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	TELEM_ENABLE_PERIODIC(telem_ctrl);
 	telem_ctrl |= ioss_period;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_WRITE,
-				    (u8 *)&telem_ctrl,
-				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_WRITE);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 		return ret;
@@ -609,8 +601,9 @@ static int telemetry_setup(struct platform_device *pdev)
 	u32 cmd[PUNIT_PARAM_LEN] = {0};
 	int ret;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
-				    NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+			&read_buf, 1, 0, 0);
 	if (ret) {
 		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
 		return ret;
@@ -713,9 +706,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		}
 
 		/* Get telemetry EVENT CTL */
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-					    &telem_ctrl, IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&telem_ctrl, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Read Failed\n");
 			goto out;
@@ -723,12 +717,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
 		/* Disable Telemetry */
 		TELEM_DISABLE(telem_ctrl);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			goto out;
@@ -740,11 +733,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		TELEM_ENABLE_PERIODIC(telem_ctrl);
 		telem_ctrl |= ioss_period;
 
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 			goto out;
@@ -1044,9 +1037,10 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 		break;
 
 	case TELEM_IOSS:
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-				IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_TRACE_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&temp, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1099,9 +1093,10 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 		break;
 
 	case TELEM_IOSS:
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-				IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_TRACE_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&temp, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1109,10 +1104,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 
 		TELEM_CLEAR_VERBOSITY_BITS(temp);
 		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
-				IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_TRACE_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
 			goto out;
@@ -1157,6 +1151,10 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
 		return PTR_ERR(punit_bios_ipc_dev);
 
+	pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+	if (IS_ERR_OR_NULL(pmc_ipc_dev))
+		return PTR_ERR(pmc_ipc_dev);
+
 	telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
 	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1236,6 +1234,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 static int telemetry_pltdrv_remove(struct platform_device *pdev)
 {
 	telemetry_clear_pltdata();
+	intel_ipc_dev_put(pmc_ipc_dev);
 	intel_ipc_dev_put(punit_bios_ipc_dev);
 	iounmap(telm_conf->pss_config.regmap);
 	iounmap(telm_conf->ioss_config.regmap);
diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
index 5aacdb0..7cc39b6 100644
--- a/include/linux/mfd/intel_soc_pmic.h
+++ b/include/linux/mfd/intel_soc_pmic.h
@@ -20,6 +20,7 @@
 #define __INTEL_SOC_PMIC_H__
 
 #include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 struct intel_soc_pmic {
 	int irq;
@@ -31,6 +32,7 @@ struct intel_soc_pmic {
 	struct regmap_irq_chip_data *irq_chip_data_chgr;
 	struct regmap_irq_chip_data *irq_chip_data_crit;
 	struct device *dev;
+	struct intel_ipc_dev *ipc_dev;
 };
 
 #endif	/* __INTEL_SOC_PMIC_H__ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs. Also, cleaned up the driver to minimize
the usage of global variable ipcdev by propogating the struct
intel_pmc_ipc_dev pointer or by getting it from device private data.

This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
 arch/x86/include/asm/intel_pmc_ipc.h          |  37 +--
 drivers/mfd/intel_soc_pmic_bxtwc.c            |  21 +-
 drivers/platform/x86/intel_pmc_ipc.c          | 393 ++++++++++----------------
 drivers/platform/x86/intel_telemetry_pltdrv.c | 119 ++++----
 include/linux/mfd/intel_soc_pmic.h            |   2 +
 5 files changed, 238 insertions(+), 334 deletions(-)

Changes since v4:
 * None

Changes since v3:
 * Added unique name to PMC regmaps.
 * Added support to clear interrupt bit.
 * Added intel_ipc_dev_put() support.

Changes since v1:
 * Removed custom APIs.
 * Cleaned up PMC IPC user drivers to use APIs provided by generic
   IPC driver.

diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index fac89eb..9fc7c3c 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -1,10 +1,15 @@
 #ifndef _ASM_X86_INTEL_PMC_IPC_H_
 #define  _ASM_X86_INTEL_PMC_IPC_H_
 
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+
+#define INTEL_PMC_IPC_DEV		"intel_pmc_ipc"
+#define PMC_PARAM_LEN			2
+
 /* Commands */
 #define PMC_IPC_PMIC_ACCESS		0xFF
-#define		PMC_IPC_PMIC_ACCESS_READ	0x0
-#define		PMC_IPC_PMIC_ACCESS_WRITE	0x1
+#define	PMC_IPC_PMIC_ACCESS_READ	0x0
+#define	PMC_IPC_PMIC_ACCESS_WRITE	0x1
 #define PMC_IPC_USB_PWR_CTRL		0xF0
 #define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
 #define PMC_IPC_PHY_CONFIG		0xEE
@@ -28,13 +33,14 @@
 #define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
 #define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
 
+static inline void pmc_cmd_init(u32 *cmd, u32 param1, u32 param2)
+{
+	cmd[0] = param1;
+	cmd[1] = param2;
+}
+
 #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
 
-int intel_pmc_ipc_simple_command(int cmd, int sub);
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen, u32 dptr, u32 sptr);
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen);
 int intel_pmc_s0ix_counter_read(u64 *data);
 int intel_pmc_gcr_read(u32 offset, u32 *data);
 int intel_pmc_gcr_write(u32 offset, u32 data);
@@ -42,23 +48,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
 
 #else
 
-static inline int intel_pmc_ipc_simple_command(int cmd, int sub)
-{
-	return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen, u32 dptr, u32 sptr)
-{
-	return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-		u32 *out, u32 outlen)
-{
-	return -EINVAL;
-}
-
 static inline int intel_pmc_s0ix_counter_read(u64 *data)
 {
 	return -EINVAL;
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 15bc052..f9df9e7 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -271,6 +271,8 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 	u8 ipc_in[2];
 	u8 ipc_out[4];
 	struct intel_soc_pmic *pmic = context;
+	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+		PMC_IPC_PMIC_ACCESS_READ};
 
 	if (!pmic)
 		return -EINVAL;
@@ -284,9 +286,8 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 
 	ipc_in[0] = reg;
 	ipc_in[1] = i2c_addr;
-	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-			PMC_IPC_PMIC_ACCESS_READ,
-			ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
+	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN, ipc_in,
+			sizeof(ipc_in), (u32 *)ipc_out, 1, 0, 0);
 	if (ret) {
 		dev_err(pmic->dev, "Failed to read from PMIC\n");
 		return ret;
@@ -303,6 +304,8 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
 	int i2c_addr;
 	u8 ipc_in[3];
 	struct intel_soc_pmic *pmic = context;
+	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+		PMC_IPC_PMIC_ACCESS_WRITE};
 
 	if (!pmic)
 		return -EINVAL;
@@ -317,9 +320,8 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
 	ipc_in[0] = reg;
 	ipc_in[1] = i2c_addr;
 	ipc_in[2] = val;
-	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-			PMC_IPC_PMIC_ACCESS_WRITE,
-			ipc_in, sizeof(ipc_in), NULL, 0);
+	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN, ipc_in,
+			sizeof(ipc_in), NULL, 0, 0, 0);
 	if (ret) {
 		dev_err(pmic->dev, "Failed to write to PMIC\n");
 		return ret;
@@ -445,6 +447,10 @@ static int bxtwc_probe(struct platform_device *pdev)
 	if (!pmic)
 		return -ENOMEM;
 
+	pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+	if (IS_ERR_OR_NULL(pmic->ipc_dev))
+		return PTR_ERR(pmic->ipc_dev);
+
 	ret = platform_get_irq(pdev, 0);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Invalid IRQ\n");
@@ -562,7 +568,10 @@ static int bxtwc_probe(struct platform_device *pdev)
 
 static int bxtwc_remove(struct platform_device *pdev)
 {
+	struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
 	sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
+	intel_ipc_dev_put(pmic->ipc_dev);
 
 	return 0;
 }
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index fd5bcdf..35d4734 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -47,18 +47,8 @@
  * The ARC handles the interrupt and services it, writing optional data to
  * the IPC1 registers, updates the IPC_STS response register with the status.
  */
-#define IPC_CMD			0x0
-#define		IPC_CMD_MSI		0x100
 #define		IPC_CMD_SIZE		16
 #define		IPC_CMD_SUBCMD		12
-#define IPC_STATUS		0x04
-#define		IPC_STATUS_IRQ		0x4
-#define		IPC_STATUS_ERR		0x2
-#define		IPC_STATUS_BUSY		0x1
-#define IPC_SPTR		0x08
-#define IPC_DPTR		0x0C
-#define IPC_WRITE_BUFFER	0x80
-#define IPC_READ_BUFFER		0x90
 
 /* Residency with clock rate at 19.2MHz to usecs */
 #define S0IX_RESIDENCY_IN_USECS(d, s)		\
@@ -73,11 +63,6 @@
  */
 #define IPC_DATA_BUFFER_SIZE	16
 
-#define IPC_LOOP_CNT		3000000
-#define IPC_MAX_SEC		3
-
-#define IPC_TRIGGER_MODE_IRQ		true
-
 /* exported resources from IFWI */
 #define PLAT_RESOURCE_IPC_INDEX		0
 #define PLAT_RESOURCE_IPC_SIZE		0x1000
@@ -117,36 +102,41 @@
 #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
 #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+/* IPC PMC commands */
+#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
+#define	IPC_DEV_PMC_CMD_SIZE			16
+#define	IPC_DEV_PMC_CMD_SUBCMD			12
+#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK		GENMASK(7, 0)
+#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
+
+/*IPC PMC reg offsets */
+#define IPC_DEV_PMC_STATUS_OFFSET		0x04
+#define IPC_DEV_PMC_SPTR_OFFSET			0x08
+#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
+#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
+#define IPC_DEV_PMC_RBUF_OFFSET			0x90
+
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
+	struct intel_ipc_dev *pmc_ipc_dev;
+	struct intel_ipc_dev_ops ops;
+	struct intel_ipc_dev_cfg cfg;
 	void __iomem *ipc_base;
-	bool irq_mode;
-	int irq;
-	int cmd;
-	struct completion cmd_complete;
 
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	struct regmap *gcr_regs;
 } ipcdev;
 
-static char *ipc_err_sources[] = {
-	[IPC_ERR_NONE] =
-		"no error",
-	[IPC_ERR_CMD_NOT_SUPPORTED] =
-		"command not supported",
-	[IPC_ERR_CMD_NOT_SERVICED] =
-		"command not serviced",
-	[IPC_ERR_UNABLE_TO_SERVICE] =
-		"unable to service",
-	[IPC_ERR_CMD_INVALID] =
-		"command invalid",
-	[IPC_ERR_CMD_FAILED] =
-		"command failed",
-	[IPC_ERR_EMSECURITY] =
-		"Invalid Battery",
-	[IPC_ERR_UNSIGNEDKERNEL] =
-		"Unsigned kernel",
+static struct regmap_config pmc_regmap_config = {
+	.name = "intel_pmc_regs",
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
 };
 
 static struct regmap_config gcr_regmap_config = {
@@ -158,40 +148,6 @@ static struct regmap_config gcr_regmap_config = {
 	.max_register = PLAT_RESOURCE_GCR_SIZE,
 };
 
-/* Prevent concurrent calls to the PMC */
-static DEFINE_MUTEX(ipclock);
-
-static inline void ipc_send_command(u32 cmd)
-{
-	ipcdev.cmd = cmd;
-	if (ipcdev.irq_mode) {
-		reinit_completion(&ipcdev.cmd_complete);
-		cmd |= IPC_CMD_MSI;
-	}
-	writel(cmd, ipcdev.ipc_base + IPC_CMD);
-}
-
-static inline u32 ipc_read_status(void)
-{
-	return readl(ipcdev.ipc_base + IPC_STATUS);
-}
-
-static inline void ipc_data_writel(u32 data, u32 offset)
-{
-	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
-}
-
-static inline u8 __maybe_unused ipc_data_readb(u32 offset)
-{
-	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-static inline u32 ipc_data_readl(u32 offset)
-{
-	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-
 /**
  * intel_pmc_gcr_read() - Read PMC GCR register
  * @offset:	offset of GCR register from GCR address base
@@ -263,160 +219,109 @@ static int update_no_reboot_bit(void *priv, bool set)
 				    PMC_CFG_NO_REBOOT_MASK, value);
 }
 
-static int intel_pmc_ipc_check_status(void)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	int status;
-	int ret = 0;
-
-	if (ipcdev.irq_mode) {
-		if (0 == wait_for_completion_timeout(
-				&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
-			ret = -ETIMEDOUT;
-	} else {
-		int loop_count = IPC_LOOP_CNT;
-
-		while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
-			udelay(1);
-		if (loop_count == 0)
-			ret = -ETIMEDOUT;
-	}
-
-	status = ipc_read_status();
-	if (ret == -ETIMEDOUT) {
-		dev_err(ipcdev.dev,
-			"IPC timed out, TS=0x%x, CMD=0x%x\n",
-			status, ipcdev.cmd);
-		return ret;
-	}
+	if (!cmd_list || cmdlen != PMC_PARAM_LEN)
+		return -EINVAL;
 
-	if (status & IPC_STATUS_ERR) {
-		int i;
-
-		ret = -EIO;
-		i = (status >> IPC_CMD_SIZE) & 0xFF;
-		if (i < ARRAY_SIZE(ipc_err_sources))
-			dev_err(ipcdev.dev,
-				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
-				ipc_err_sources[i], status, ipcdev.cmd);
-		else
-			dev_err(ipcdev.dev,
-				"IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
-				status, ipcdev.cmd);
-		if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
-			ret = -EACCES;
-	}
+	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
 
-	return ret;
+	return 0;
 }
 
-/**
- * intel_pmc_ipc_simple_command() - Simple IPC command
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- *
- * Send a simple IPC command to PMC when don't need to specify
- * input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_simple_command(int cmd, int sub)
+static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+		u32 *out, u32 outlen, u32 dptr, u32 sptr)
 {
 	int ret;
 
-	mutex_lock(&ipclock);
-	if (ipcdev.dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
-	ret = intel_pmc_ipc_check_status();
-	mutex_unlock(&ipclock);
+	if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE/4)
+		return -EINVAL;
 
-	return ret;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
 
-/**
- * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in bytes.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- * @sptr:	data writing to SPTR register.
- * @dptr:	data writing to DPTR register.
- *
- * Send an IPC command to PMC with input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
-			  u32 outlen, u32 dptr, u32 sptr)
-{
-	u32 wbuf[4] = { 0 };
-	int ret;
-	int i;
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
 
-	if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
-		return -EINVAL;
+	return 0;
+}
 
-	mutex_lock(&ipclock);
-	if (ipcdev.dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	memcpy(wbuf, in, inlen);
-	writel(dptr, ipcdev.ipc_base + IPC_DPTR);
-	writel(sptr, ipcdev.ipc_base + IPC_SPTR);
-	/* The input data register is 32bit register and inlen is in Byte */
-	for (i = 0; i < ((inlen + 3) / 4); i++)
-		ipc_data_writel(wbuf[i], 4 * i);
-	ipc_send_command((inlen << IPC_CMD_SIZE) |
-			(sub << IPC_CMD_SUBCMD) | cmd);
-	ret = intel_pmc_ipc_check_status();
-	if (!ret) {
-		/* out is read from 32bit register and outlen is in 32bit */
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(4 * i);
-	}
-	mutex_unlock(&ipclock);
+static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq)
+{
+	return regmap_write_bits(ipc_dev->cfg->cmd_regs,
+				  ipc_dev->cfg->status_reg,
+				  IPC_DEV_PMC_CMD_STATUS_IRQ,
+				  IPC_DEV_PMC_CMD_STATUS_IRQ);
+}
 
-	return ret;
+static int pmc_ipc_err_code(int status)
+{
+	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
+			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
 
-/**
- * intel_pmc_ipc_command() -  IPC command with input/output data
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in bytes.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- *
- * Send an IPC command to PMC with input/output data.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-			  u32 *out, u32 outlen)
+static int pmc_ipc_busy_check(int status)
 {
-	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
 
-static irqreturn_t ioc(int irq, void *dev_id)
+static u32 pmc_ipc_enable_msi(u32 cmd)
 {
-	int status;
+	return cmd | IPC_DEV_PMC_CMD_MSI;
+}
 
-	if (ipcdev.irq_mode) {
-		status = ipc_read_status();
-		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
-	}
-	complete(&ipcdev.cmd_complete);
+static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
+		struct device *pmc_dev,
+		void __iomem *base,
+		int irq)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *cmd_regs;
+
+	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+        cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
+			&pmc_regmap_config);
+        if (IS_ERR(cmd_regs)) {
+                dev_err(pmc_dev, "cmd_regs regmap init failed\n");
+                return ERR_CAST(cmd_regs);;
+        }
 
-	return IRQ_HANDLED;
+	/* set IPC dev ops */
+	ops->to_err_code = pmc_ipc_err_code;
+	ops->busy_check = pmc_ipc_busy_check;
+	ops->enable_msi = pmc_ipc_enable_msi;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+	ops->pre_irq_handler_fn = pre_irq_handler_fn;
+
+	/* set cfg options */
+	if (irq > 0)
+		cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+		cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PMC;
+	cfg->irq = irq;
+	cfg->use_msi = true;
+	cfg->support_sptr = true;
+	cfg->support_dptr = true;
+	cfg->cmd_regs = cmd_regs;
+	cfg->data_regs = cmd_regs;
+	cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
+	cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
+	cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
+	cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
+	cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
+
+	return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV, cfg, ops);
 }
 
 static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -428,8 +333,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (pmc->dev)
 		return -EBUSY;
 
-	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
-
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
@@ -438,15 +341,14 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (ret)
 		return ret;
 
-	init_completion(&pmc->cmd_complete);
-
 	pmc->ipc_base = pcim_iomap_table(pdev)[0];
 
-	ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
-				pmc);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		return ret;
+	pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			pmc->ipc_base, pdev->irq);
+	if (IS_ERR(pmc->pmc_ipc_dev)) {
+		dev_err(&pdev->dev,
+				"Failed to create PMC IPC device\n");
+		return PTR_ERR(pmc->pmc_ipc_dev);
 	}
 
 	pmc->dev = &pdev->dev;
@@ -474,19 +376,19 @@ static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
 					      struct device_attribute *attr,
 					      const char *buf, size_t count)
 {
-	int subcmd;
-	int cmd;
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
+	int cmd[2];
 	int ret;
 
-	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+	ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
 	if (ret != 2) {
 		dev_err(dev, "Error args\n");
 		return -EINVAL;
 	}
 
-	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
+	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
 	if (ret) {
-		dev_err(dev, "command %d error with %d\n", cmd, ret);
+		dev_err(dev, "command %d error with %d\n", cmd[0], ret);
 		return ret;
 	}
 	return (ssize_t)count;
@@ -496,22 +398,23 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
 					     struct device_attribute *attr,
 					     const char *buf, size_t count)
 {
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
 	unsigned long val;
-	int subcmd;
+	int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
 	int ret;
 
 	if (kstrtoul(buf, 0, &val))
 		return -EINVAL;
 
 	if (val)
-		subcmd = 1;
-	else
-		subcmd = 0;
-	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
+		cmd[1] = 1;
+
+	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
 	if (ret) {
-		dev_err(dev, "command north %d error with %d\n", subcmd, ret);
+		dev_err(dev, "command north %d error with %d\n", cmd[1], ret);
 		return ret;
 	}
+
 	return (ssize_t)count;
 }
 
@@ -689,6 +592,7 @@ static int ipc_create_pmc_devices(struct platform_device *pdev)
 
 static int ipc_plat_get_res(struct platform_device *pdev)
 {
+	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
 	struct resource *res;
 	void __iomem *addr;
 
@@ -707,9 +611,9 @@ static int ipc_plat_get_res(struct platform_device *pdev)
 	if (IS_ERR(addr))
 		return PTR_ERR(addr);
 
-	ipcdev.ipc_base = addr;
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
+	pmc->ipc_base = addr;
+	pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
+	dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
 
 	return 0;
 }
@@ -755,14 +659,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
 
 static int ipc_plat_probe(struct platform_device *pdev)
 {
-	int ret;
+	int ret, irq;
+	struct intel_pmc_ipc_dev *pmc = &ipcdev;
+
+	pmc->dev = &pdev->dev;
 
-	ipcdev.dev = &pdev->dev;
-	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
-	init_completion(&ipcdev.cmd_complete);
+	dev_set_drvdata(&pdev->dev, pmc);
 
-	ipcdev.irq = platform_get_irq(pdev, 0);
-	if (ipcdev.irq < 0) {
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
 		dev_err(&pdev->dev, "Failed to get irq\n");
 		return -EINVAL;
 	}
@@ -773,11 +678,11 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
-			ipcdev.gcr_mem_base, &gcr_regmap_config);
-        if (IS_ERR(ipcdev.gcr_regs)) {
-                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
-                return PTR_ERR(ipcdev.gcr_regs);;
+        pmc->gcr_regs = devm_regmap_init_mmio_clk(pmc->dev, NULL,
+			pmc->gcr_mem_base, &gcr_regmap_config);
+        if (IS_ERR(pmc->gcr_regs)) {
+                dev_err(&pdev->dev, "gcr_regs regmap init failed\n");
+                return PTR_ERR(pmc->gcr_regs);;
         }
 
 	ret = ipc_create_pmc_devices(pdev);
@@ -786,20 +691,20 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
-			     "intel_pmc_ipc", &ipcdev)) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		return -EBUSY;
-	}
-
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
 		return ret;
 	}
 
+	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			pmc->ipc_base, irq);
+	if (IS_ERR(pmc->pmc_ipc_dev)) {
+		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
+		return PTR_ERR(pmc->pmc_ipc_dev);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index bf8284a..a08a620 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -56,10 +56,6 @@
 #define IOSS_TELEM_TRACE_CTL_WRITE	0x6
 #define IOSS_TELEM_EVENT_CTL_READ	0x7
 #define IOSS_TELEM_EVENT_CTL_WRITE	0x8
-#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE	0x4
-#define IOSS_TELEM_READ_WORD		0x1
-#define IOSS_TELEM_WRITE_FOURBYTES	0x4
-#define IOSS_TELEM_EVT_WRITE_SIZE	0x3
 
 #define TELEM_INFO_SRAMEVTS_MASK	0xFF00
 #define TELEM_INFO_SRAMEVTS_SHIFT	0x8
@@ -98,7 +94,7 @@ struct telem_ssram_region {
 };
 
 static struct telemetry_plt_config *telm_conf;
-static struct intel_ipc_dev *punit_bios_ipc_dev;
+static struct intel_ipc_dev *punit_bios_ipc_dev, *pmc_ipc_dev;
 
 /*
  * The following counters are programmed by default during setup.
@@ -267,17 +263,16 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
 static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
 {
 	u32 write_buf;
-	int ret;
+	u32 cmd[PMC_PARAM_LEN] = {0};
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
 	write_buf <<= BITS_PER_BYTE;
 	write_buf |= index;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
-				    IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_WRITE);
 
-	return ret;
+	return ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
 
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -287,6 +282,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 
 	write_buf = evt_id | TELEM_EVENT_ENABLE;
 	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
+
 	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd, PUNIT_PARAM_LEN,
 			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
 }
@@ -298,15 +294,16 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	int ret, index, idx;
 	u32 *ioss_evtmap;
 	u32 telem_ctrl;
+	u32 cmd[PMC_PARAM_LEN] = {0};
 
 	num_ioss_evts = evtconfig.num_evts;
 	ioss_period = evtconfig.period;
 	ioss_evtmap = evtconfig.evtmap;
 
 	/* Get telemetry EVENT CTL */
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-				    &telem_ctrl, IOSS_TELEM_READ_WORD);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_READ);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+			&telem_ctrl, 1, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Read Failed\n");
 		return ret;
@@ -314,12 +311,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 
 	/* Disable Telemetry */
 	TELEM_DISABLE(telem_ctrl);
-
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_WRITE,
-				    (u8 *)&telem_ctrl,
-				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-				    NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_WRITE);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 		return ret;
@@ -330,12 +324,11 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	if (action == TELEM_RESET) {
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl),
+				NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -360,11 +353,11 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 		/* Clear All Events */
 		TELEM_CLEAR_EVENTS(telem_ctrl);
 
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			return ret;
@@ -412,10 +405,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	TELEM_ENABLE_PERIODIC(telem_ctrl);
 	telem_ctrl |= ioss_period;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				    IOSS_TELEM_EVENT_CTL_WRITE,
-				    (u8 *)&telem_ctrl,
-				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_EVENT_CTL_WRITE);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 		return ret;
@@ -609,8 +601,9 @@ static int telemetry_setup(struct platform_device *pdev)
 	u32 cmd[PUNIT_PARAM_LEN] = {0};
 	int ret;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
-				    NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
+	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ);
+	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+			&read_buf, 1, 0, 0);
 	if (ret) {
 		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
 		return ret;
@@ -713,9 +706,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		}
 
 		/* Get telemetry EVENT CTL */
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-					    &telem_ctrl, IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&telem_ctrl, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Read Failed\n");
 			goto out;
@@ -723,12 +717,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
 		/* Disable Telemetry */
 		TELEM_DISABLE(telem_ctrl);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			goto out;
@@ -740,11 +733,11 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 		TELEM_ENABLE_PERIODIC(telem_ctrl);
 		telem_ctrl |= ioss_period;
 
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-					    NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_EVENT_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
+				0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 			goto out;
@@ -1044,9 +1037,10 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
 		break;
 
 	case TELEM_IOSS:
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-				IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_TRACE_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&temp, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1099,9 +1093,10 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 		break;
 
 	case TELEM_IOSS:
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-				IOSS_TELEM_READ_WORD);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
+				IOSS_TELEM_TRACE_CTL_READ);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL, 0,
+				&temp, 1, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1109,10 +1104,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
 
 		TELEM_CLEAR_VERBOSITY_BITS(temp);
 		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
-
-		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-				IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
-				IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
+		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_TRACE_CTL_WRITE);
+		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
+				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
 			goto out;
@@ -1157,6 +1151,10 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
 		return PTR_ERR(punit_bios_ipc_dev);
 
+	pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+	if (IS_ERR_OR_NULL(pmc_ipc_dev))
+		return PTR_ERR(pmc_ipc_dev);
+
 	telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
 	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1236,6 +1234,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 static int telemetry_pltdrv_remove(struct platform_device *pdev)
 {
 	telemetry_clear_pltdata();
+	intel_ipc_dev_put(pmc_ipc_dev);
 	intel_ipc_dev_put(punit_bios_ipc_dev);
 	iounmap(telm_conf->pss_config.regmap);
 	iounmap(telm_conf->ioss_config.regmap);
diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
index 5aacdb0..7cc39b6 100644
--- a/include/linux/mfd/intel_soc_pmic.h
+++ b/include/linux/mfd/intel_soc_pmic.h
@@ -20,6 +20,7 @@
 #define __INTEL_SOC_PMIC_H__
 
 #include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 struct intel_soc_pmic {
 	int irq;
@@ -31,6 +32,7 @@ struct intel_soc_pmic {
 	struct regmap_irq_chip_data *irq_chip_data_chgr;
 	struct regmap_irq_chip_data *irq_chip_data_crit;
 	struct device *dev;
+	struct intel_ipc_dev *ipc_dev;
 };
 
 #endif	/* __INTEL_SOC_PMIC_H__ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 8/8] platform/x86: intel_scu_ipc: Use generic Intel IPC device calls
  2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs.

This patch also cleans-up SCU IPC user drivers(rtc-mrst.c, intel-mid_wdt.c,
intel-mid_wdt.c, intel-mid.c) to use APIs provided by generic IPC driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 arch/x86/include/asm/intel_scu_ipc.h    |  23 +-
 arch/x86/platform/intel-mid/intel-mid.c |  15 +-
 drivers/platform/x86/Kconfig            |   1 +
 drivers/platform/x86/intel_scu_ipc.c    | 483 +++++++++++++-------------------
 drivers/rtc/rtc-mrst.c                  |  16 +-
 drivers/watchdog/intel-mid_wdt.c        |  12 +-
 drivers/watchdog/intel_scu_watchdog.c   |  18 +-
 7 files changed, 256 insertions(+), 312 deletions(-)

Changes since v4:
 * None

Changes since v3:
 * Added intel_ipc_dev_put() support.

diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 81d3d87..5842534 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -14,9 +14,19 @@
 #define IPCMSG_COLD_BOOT	0xF3
 
 #define IPCMSG_VRTC		0xFA	 /* Set vRTC device */
-	/* Command id associated with message IPCMSG_VRTC */
-	#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
-	#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
+
+/* Command id associated with message IPCMSG_VRTC */
+#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
+#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
+
+#define INTEL_SCU_IPC_DEV	"intel_scu_ipc"
+#define SCU_PARAM_LEN		2
+
+static inline void scu_cmd_init(u32 *cmd, u32 param1, u32 param2)
+{
+	cmd[0] = param1;
+	cmd[1] = param2;
+}
 
 /* Read single register */
 int intel_scu_ipc_ioread8(u16 addr, u8 *data);
@@ -45,13 +55,6 @@ int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
 /* Update single register based on the mask */
 int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
 
-/* Issue commands to the SCU with or without data */
-int intel_scu_ipc_simple_command(int cmd, int sub);
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-			  u32 *out, int outlen);
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
-			      u32 *out, int outlen, u32 dptr, u32 sptr);
-
 /* I2C control api */
 int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
 
diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c
index 86676ce..23b3ab3 100644
--- a/arch/x86/platform/intel-mid/intel-mid.c
+++ b/arch/x86/platform/intel-mid/intel-mid.c
@@ -22,6 +22,7 @@
 #include <linux/irq.h>
 #include <linux/export.h>
 #include <linux/notifier.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -70,16 +71,26 @@ EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip);
 
 static void intel_mid_power_off(void)
 {
+	struct intel_ipc_dev *ipc_dev;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_OFF, 1};
+
 	/* Shut down South Complex via PWRMU */
 	intel_mid_pwr_power_off();
 
+	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
 	/* Only for Tangier, the rest will ignore this command */
-	intel_scu_ipc_simple_command(IPCMSG_COLD_OFF, 1);
+	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+	intel_ipc_dev_put(ipc_dev);
 };
 
 static void intel_mid_reboot(void)
 {
-	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
+	struct intel_ipc_dev *ipc_dev;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_BOOT, 0};
+
+	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+	intel_ipc_dev_put(ipc_dev);
 }
 
 static unsigned long __init intel_mid_calibrate_tsc(void)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 9442c23..1b9093c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -863,6 +863,7 @@ config INTEL_VBTN
 config INTEL_SCU_IPC
 	bool "Intel SCU IPC Support"
 	depends on X86_INTEL_MID
+	select REGMAP_MMIO
 	default y
 	---help---
 	  IPC is used to bridge the communications between kernel and SCU on
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 2c85f75..8812202 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -24,6 +24,8 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/sfi.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 #include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
@@ -39,6 +41,25 @@
 #define IPC_CMD_PCNTRL_R      1 /* Register read */
 #define IPC_CMD_PCNTRL_M      2 /* Register read-modify-write */
 
+/* IPC dev register offsets */
+/*
+ * IPC Read Buffer (Read Only):
+ * 16 byte buffer for receiving data from SCU, if IPC command
+ * processing results in response data
+ */
+#define IPC_DEV_SCU_RBUF_OFFSET			0x90
+#define IPC_DEV_SCU_WRBUF_OFFSET		0x80
+#define IPC_DEV_SCU_SPTR_OFFSET			0x08
+#define IPC_DEV_SCU_DPTR_OFFSET			0x0C
+#define IPC_DEV_SCU_STATUS_OFFSET		0x04
+
+/* IPC dev commands */
+/* IPC command register IOC bit */
+#define	IPC_DEV_SCU_CMD_MSI			BIT(8)
+#define	IPC_DEV_SCU_CMD_STATUS_ERR		BIT(1)
+#define	IPC_DEV_SCU_CMD_STATUS_ERR_MASK		GENMASK(7, 0)
+#define	IPC_DEV_SCU_CMD_STATUS_BUSY		BIT(0)
+
 /*
  * IPC register summary
  *
@@ -59,6 +80,11 @@
 #define IPC_WWBUF_SIZE    20		/* IPC Write buffer Size */
 #define IPC_RWBUF_SIZE    20		/* IPC Read buffer Size */
 #define IPC_IOC	          0x100		/* IPC command register IOC bit */
+#define	IPC_CMD_SIZE            16
+#define	IPC_CMD_SUBCMD          12
+#define	IPC_RWBUF_SIZE_DWORD    5
+#define	IPC_WWBUF_SIZE_DWORD    5
+
 
 #define PCI_DEVICE_ID_LINCROFT		0x082a
 #define PCI_DEVICE_ID_PENWELL		0x080e
@@ -93,140 +119,49 @@ static const struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
 
 struct intel_scu_ipc_dev {
 	struct device *dev;
+	struct intel_ipc_dev *ipc_dev;
 	void __iomem *ipc_base;
 	void __iomem *i2c_base;
-	struct completion cmd_complete;
+	struct regmap *ipc_regs;
+	struct regmap *i2c_regs;
 	u8 irq_mode;
 };
 
-static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
+static struct regmap_config ipc_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+};
 
-/*
- * IPC Read Buffer (Read Only):
- * 16 byte buffer for receiving data from SCU, if IPC command
- * processing results in response data
- */
-#define IPC_READ_BUFFER		0x90
+static struct regmap_config i2c_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
+};
+
+static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
 
 #define IPC_I2C_CNTRL_ADDR	0
 #define I2C_DATA_ADDR		0x04
 
-static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
-
-/*
- * Send ipc command
- * Command Register (Write Only):
- * A write to this register results in an interrupt to the SCU core processor
- * Format:
- * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
- */
-static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd)
-{
-	if (scu->irq_mode) {
-		reinit_completion(&scu->cmd_complete);
-		writel(cmd | IPC_IOC, scu->ipc_base);
-	}
-	writel(cmd, scu->ipc_base);
-}
-
-/*
- * Write ipc data
- * IPC Write Buffer (Write Only):
- * 16-byte buffer for sending data associated with IPC command to
- * SCU. Size of the data is specified in the IPC_COMMAND_REG register
- */
-static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset)
-{
-	writel(data, scu->ipc_base + 0x80 + offset);
-}
-
-/*
- * Status Register (Read Only):
- * Driver will read this register to get the ready/busy status of the IPC
- * block and error status of the IPC command that was just processed by SCU
- * Format:
- * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
- */
-static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu)
-{
-	return __raw_readl(scu->ipc_base + 0x04);
-}
-
-/* Read ipc byte data */
-static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset)
-{
-	return readb(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Read ipc u32 data */
-static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset)
-{
-	return readl(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Wait till scu status is busy */
-static inline int busy_loop(struct intel_scu_ipc_dev *scu)
-{
-	u32 status = ipc_read_status(scu);
-	u32 loop_count = 100000;
-
-	/* break if scu doesn't reset busy bit after huge retry */
-	while ((status & BIT(0)) && --loop_count) {
-		udelay(1); /* scu processing time is in few u secods */
-		status = ipc_read_status(scu);
-	}
-
-	if (status & BIT(0)) {
-		dev_err(scu->dev, "IPC timed out");
-		return -ETIMEDOUT;
-	}
-
-	if (status & BIT(1))
-		return -EIO;
-
-	return 0;
-}
-
-/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
-static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
-{
-	int status;
-
-	if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
-		dev_err(scu->dev, "IPC timed out\n");
-		return -ETIMEDOUT;
-	}
-
-	status = ipc_read_status(scu);
-	if (status & BIT(1))
-		return -EIO;
-
-	return 0;
-}
-
-static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
-{
-	return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
-}
-
 /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
 static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 {
 	struct intel_scu_ipc_dev *scu = &ipcdev;
 	int nc;
 	u32 offset = 0;
-	int err;
+	int err = -EIO;
 	u8 cbuf[IPC_WWBUF_SIZE];
 	u32 *wbuf = (u32 *)&cbuf;
+	u32 cmd[SCU_PARAM_LEN] = {0};
+	/* max rbuf size is 20 bytes */
+	u8 rbuf[IPC_RWBUF_SIZE] = {0};
+	u32 rbuflen = DIV_ROUND_UP(count, 4);
 
 	memset(cbuf, 0, sizeof(cbuf));
 
-	mutex_lock(&ipclock);
-
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
+	scu_cmd_init(cmd, op, id);
 
 	for (nc = 0; nc < count; nc++, offset += 2) {
 		cbuf[offset] = addr[nc];
@@ -234,30 +169,30 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 	}
 
 	if (id == IPC_CMD_PCNTRL_R) {
-		for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
-			ipc_data_writel(scu, wbuf[nc], offset);
-		ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, count * 2, (u32 *)rbuf,
+				IPC_RWBUF_SIZE_DWORD, 0, 0);
 	} else if (id == IPC_CMD_PCNTRL_W) {
 		for (nc = 0; nc < count; nc++, offset += 1)
 			cbuf[offset] = data[nc];
-		for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
-			ipc_data_writel(scu, wbuf[nc], offset);
-		ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, count * 3, NULL, 0, 0, 0);
+
 	} else if (id == IPC_CMD_PCNTRL_M) {
 		cbuf[offset] = data[0];
 		cbuf[offset + 1] = data[1];
-		ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */
-		ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, 4, NULL, 0, 0, 0);
 	}
 
-	err = intel_scu_ipc_check_status(scu);
 	if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
 		/* Workaround: values are read as 0 without memcpy_fromio */
 		memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16);
-		for (nc = 0; nc < count; nc++)
-			data[nc] = ipc_data_readb(scu, nc);
+		regmap_bulk_read(scu->ipc_regs, IPC_DEV_SCU_RBUF_OFFSET,
+					rbuf, rbuflen);
+		memcpy(data, rbuf, count);
 	}
-	mutex_unlock(&ipclock);
+
 	return err;
 }
 
@@ -422,138 +357,6 @@ int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
 }
 EXPORT_SYMBOL(intel_scu_ipc_update_register);
 
-/**
- *	intel_scu_ipc_simple_command	-	send a simple command
- *	@cmd: command
- *	@sub: sub type
- *
- *	Issue a simple command to the SCU. Do not use this interface if
- *	you must then access data as any data values may be overwritten
- *	by another SCU access by the time this function returns.
- *
- *	This function may sleep. Locking for SCU accesses is handled for
- *	the caller.
- */
-int intel_scu_ipc_simple_command(int cmd, int sub)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int err;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	ipc_command(scu, sub << 12 | cmd);
-	err = intel_scu_ipc_check_status(scu);
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
-
-/**
- *	intel_scu_ipc_command	-	command with data
- *	@cmd: command
- *	@sub: sub type
- *	@in: input data
- *	@inlen: input length in dwords
- *	@out: output data
- *	@outlein: output length in dwords
- *
- *	Issue a command to the SCU which involves data transfers. Do the
- *	data copies under the lock but leave it for the caller to interpret
- */
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-			  u32 *out, int outlen)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int i, err;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-
-	for (i = 0; i < inlen; i++)
-		ipc_data_writel(scu, *in++, 4 * i);
-
-	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
-	err = intel_scu_ipc_check_status(scu);
-
-	if (!err) {
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(scu, 4 * i);
-	}
-
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_command);
-
-#define IPC_SPTR		0x08
-#define IPC_DPTR		0x0C
-
-/**
- * intel_scu_ipc_raw_command() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in dwords.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- * @sptr:	data writing to SPTR register.
- * @dptr:	data writing to DPTR register.
- *
- * Send an IPC command to SCU with input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
-			      u32 *out, int outlen, u32 dptr, u32 sptr)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int inbuflen = DIV_ROUND_UP(inlen, 4);
-	u32 inbuf[4];
-	int i, err;
-
-	/* Up to 16 bytes */
-	if (inbuflen > 4)
-		return -EINVAL;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-
-	writel(dptr, scu->ipc_base + IPC_DPTR);
-	writel(sptr, scu->ipc_base + IPC_SPTR);
-
-	/*
-	 * SRAM controller doesn't support 8-bit writes, it only
-	 * supports 32-bit writes, so we have to copy input data into
-	 * the temporary buffer, and SCU FW will use the inlen to
-	 * determine the actual input data length in the temporary
-	 * buffer.
-	 */
-	memcpy(inbuf, in, inlen);
-
-	for (i = 0; i < inbuflen; i++)
-		ipc_data_writel(scu, inbuf[i], 4 * i);
-
-	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
-	err = intel_scu_ipc_check_status(scu);
-	if (!err) {
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(scu, 4 * i);
-	}
-
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL_GPL(intel_scu_ipc_raw_command);
-
 /* I2C commands */
 #define IPC_I2C_WRITE 1 /* I2C Write command */
 #define IPC_I2C_READ  2 /* I2C Read command */
@@ -575,48 +378,143 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
 	struct intel_scu_ipc_dev *scu = &ipcdev;
 	u32 cmd = 0;
 
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
 	cmd = (addr >> 24) & 0xFF;
 	if (cmd == IPC_I2C_READ) {
-		writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+		regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
 		/* Write not getting updated without delay */
 		mdelay(1);
-		*data = readl(scu->i2c_base + I2C_DATA_ADDR);
+		regmap_read(scu->i2c_regs, I2C_DATA_ADDR, data);
 	} else if (cmd == IPC_I2C_WRITE) {
-		writel(*data, scu->i2c_base + I2C_DATA_ADDR);
+		regmap_write(scu->i2c_regs, I2C_DATA_ADDR, *data);
 		mdelay(1);
-		writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+		regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
 	} else {
 		dev_err(scu->dev,
 			"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
 
-		mutex_unlock(&ipclock);
 		return -EIO;
 	}
-	mutex_unlock(&ipclock);
 	return 0;
 }
 EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
 
-/*
- * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
- * When ioc bit is set to 1, caller api must wait for interrupt handler called
- * which in turn unlocks the caller api. Currently this is not used
- *
- * This is edge triggered so we need take no action to clear anything
- */
-static irqreturn_t ioc(int irq, void *dev_id)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	struct intel_scu_ipc_dev *scu = dev_id;
+	if (!cmd_list || cmdlen != SCU_PARAM_LEN)
+		return -EINVAL;
 
-	if (scu->irq_mode)
-		complete(&scu->cmd_complete);
+	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
 
-	return IRQ_HANDLED;
+	return 0;
+}
+
+static int pre_cmd_fn(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
+		u32 *out, u32 outlen)
+{
+	int ret;
+
+	if (inlen > IPC_WWBUF_SIZE_DWORD || outlen > IPC_RWBUF_SIZE_DWORD)
+		return -EINVAL;
+
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
+
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
+
+	return 0;
+}
+
+static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+		u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	int ret;
+
+	if (inlen > IPC_WWBUF_SIZE || outlen > IPC_RWBUF_SIZE_DWORD)
+		return -EINVAL;
+
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
+
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
+
+	return 0;
+}
+
+static int scu_ipc_err_code(int status)
+{
+	if (status & IPC_DEV_SCU_CMD_STATUS_ERR)
+		return (status & IPC_DEV_SCU_CMD_STATUS_ERR_MASK);
+	else
+		return 0;
+}
+
+static int scu_ipc_busy_check(int status)
+{
+	return status | IPC_DEV_SCU_CMD_STATUS_BUSY;
+}
+
+static u32 scu_ipc_enable_msi(u32 cmd)
+{
+	return cmd | IPC_DEV_SCU_CMD_MSI;
+}
+
+static struct intel_ipc_dev *intel_scu_ipc_dev_create(
+		struct device *dev,
+		void __iomem *base,
+		int irq)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *ipc_regs;
+	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
+
+	cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+        ipc_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
+			&ipc_regmap_config);
+        if (IS_ERR(ipc_regs)) {
+                dev_err(dev, "ipc_regs regmap init failed\n");
+                return ERR_CAST(ipc_regs);;
+        }
+
+	scu->ipc_regs = ipc_regs;
+
+	/* set IPC dev ops */
+	ops->to_err_code = scu_ipc_err_code;
+	ops->busy_check = scu_ipc_busy_check;
+	ops->enable_msi = scu_ipc_enable_msi;
+	ops->pre_cmd_fn = pre_cmd_fn;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+
+	/* set cfg options */
+	if (scu->irq_mode)
+		cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+		cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_SCU;
+	cfg->irq = irq;
+	cfg->use_msi = true;
+	cfg->support_sptr = true;
+	cfg->support_dptr = true;
+	cfg->cmd_regs = ipc_regs;
+	cfg->data_regs = ipc_regs;
+	cfg->wrbuf_reg = IPC_DEV_SCU_WRBUF_OFFSET;
+	cfg->rbuf_reg = IPC_DEV_SCU_RBUF_OFFSET;
+	cfg->sptr_reg = IPC_DEV_SCU_SPTR_OFFSET;
+	cfg->dptr_reg = IPC_DEV_SCU_DPTR_OFFSET;
+	cfg->status_reg = IPC_DEV_SCU_STATUS_OFFSET;
+
+	return devm_intel_ipc_dev_create(dev, INTEL_SCU_IPC_DEV, cfg, ops);
 }
 
 /**
@@ -650,25 +548,34 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		return err;
 
-	init_completion(&scu->cmd_complete);
-
 	scu->ipc_base = pcim_iomap_table(pdev)[0];
 
-	scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
+	scu->i2c_base = devm_ioremap_nocache(&pdev->dev, pdata->i2c_base,
+			pdata->i2c_len);
 	if (!scu->i2c_base)
 		return -ENOMEM;
 
-	err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
-			       scu);
-	if (err)
-		return err;
+	pci_set_drvdata(pdev, scu);
+
+        scu->i2c_regs = devm_regmap_init_mmio_clk(&pdev->dev, NULL,
+			scu->i2c_base, &i2c_regmap_config);
+        if (IS_ERR(scu->i2c_regs)) {
+                dev_err(&pdev->dev, "i2c_regs regmap init failed\n");
+                return PTR_ERR(scu->i2c_regs);;
+        }
+
+	scu->ipc_dev = intel_scu_ipc_dev_create(&pdev->dev, scu->ipc_base,
+			pdev->irq);
+	if (IS_ERR(scu->ipc_dev)) {
+		dev_err(&pdev->dev, "Failed to create SCU IPC device\n");
+		return PTR_ERR(scu->ipc_dev);
+	}
 
 	/* Assign device at last */
 	scu->dev = &pdev->dev;
 
 	intel_scu_devices_create();
 
-	pci_set_drvdata(pdev, scu);
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index 7334c44..37595c4 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -36,6 +36,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/sfi.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/intel_scu_ipc.h>
 #include <asm/intel-mid.h>
@@ -46,6 +47,7 @@ struct mrst_rtc {
 	struct device		*dev;
 	int			irq;
 	struct resource		*iomem;
+	struct intel_ipc_dev	*ipc_dev;
 
 	u8			enabled_wake;
 	u8			suspend_ctrl;
@@ -110,10 +112,11 @@ static int mrst_read_time(struct device *dev, struct rtc_time *time)
 
 static int mrst_set_time(struct device *dev, struct rtc_time *time)
 {
-	int ret;
 	unsigned long flags;
 	unsigned char mon, day, hrs, min, sec;
 	unsigned int yrs;
+	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME};
 
 	yrs = time->tm_year;
 	mon = time->tm_mon + 1;   /* tm_mon starts at zero */
@@ -137,8 +140,7 @@ static int mrst_set_time(struct device *dev, struct rtc_time *time)
 
 	spin_unlock_irqrestore(&rtc_lock, flags);
 
-	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
-	return ret;
+	return ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
 }
 
 static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
@@ -210,6 +212,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
 	unsigned char hrs, min, sec;
 	int ret = 0;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM};
 
 	if (!mrst->irq)
 		return -EIO;
@@ -229,7 +232,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 
 	spin_unlock_irq(&rtc_lock);
 
-	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
+	ret = ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
 	if (ret)
 		return ret;
 
@@ -329,6 +332,10 @@ static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
 	if (!iomem)
 		return -ENODEV;
 
+	mrst_rtc.ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(mrst_rtc.ipc_dev))
+		return PTR_ERR(mrst_rtc.ipc_dev);
+
 	iomem = request_mem_region(iomem->start, resource_size(iomem),
 				   driver_name);
 	if (!iomem) {
@@ -394,6 +401,7 @@ static void rtc_mrst_do_remove(struct device *dev)
 
 	rtc_mrst_do_shutdown();
 
+	intel_ipc_dev_put(mrst->ipc_dev);
 	if (mrst->irq)
 		free_irq(mrst->irq, mrst->rtc);
 
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 72c108a..0d455bd 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -18,6 +18,7 @@
 #include <linux/platform_device.h>
 #include <linux/watchdog.h>
 #include <linux/platform_data/intel-mid_wdt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/intel_scu_ipc.h>
 #include <asm/intel-mid.h>
@@ -29,6 +30,8 @@
 #define MID_WDT_TIMEOUT_MAX		170
 #define MID_WDT_DEFAULT_TIMEOUT		90
 
+static struct intel_ipc_dev *scu_ipc_dev;
+
 /* SCU watchdog messages */
 enum {
 	SCU_WATCHDOG_START = 0,
@@ -38,7 +41,10 @@ enum {
 
 static inline int wdt_command(int sub, u32 *in, int inlen)
 {
-	return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+	u32 cmds[SCU_PARAM_LEN] = {IPC_WATCHDOG, sub};
+
+	return ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, in,
+			inlen, NULL, 0);
 }
 
 static int wdt_start(struct watchdog_device *wd)
@@ -129,6 +135,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	if (!wdt_dev)
 		return -ENOMEM;
 
+	scu_ipc_dev = devm_intel_ipc_dev_get(&pdev->dev, INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(scu_ipc_dev))
+		return PTR_ERR(scu_ipc_dev);
+
 	wdt_dev->info = &mid_wdt_info;
 	wdt_dev->ops = &mid_wdt_ops;
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index 0caab62..55169cb 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -49,6 +49,7 @@
 #include <asm/intel_scu_ipc.h>
 #include <asm/apb_timer.h>
 #include <asm/intel-mid.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include "intel_scu_watchdog.h"
 
@@ -94,6 +95,8 @@ MODULE_PARM_DESC(force_boot,
 
 static struct intel_scu_watchdog_dev watchdog_device;
 
+static struct intel_ipc_dev *scu_ipc_dev;
+
 /* Forces restart, if force_reboot is set */
 static void watchdog_fire(void)
 {
@@ -128,18 +131,14 @@ static int watchdog_set_ipc(int soft_threshold, int threshold)
 	u32	*ipc_wbuf;
 	u8	 cbuf[16] = { '\0' };
 	int	 ipc_ret = 0;
+	u32 cmds[SCU_PARAM_LEN] = {IPC_SET_WATCHDOG_TIMER, 0};
 
 	ipc_wbuf = (u32 *)&cbuf;
 	ipc_wbuf[0] = soft_threshold;
 	ipc_wbuf[1] = threshold;
 
-	ipc_ret = intel_scu_ipc_command(
-			IPC_SET_WATCHDOG_TIMER,
-			0,
-			ipc_wbuf,
-			2,
-			NULL,
-			0);
+	ipc_ret = ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, ipc_wbuf,
+			2, NULL, 0);
 
 	if (ipc_ret != 0)
 		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
@@ -460,6 +459,10 @@ static int __init intel_scu_watchdog_init(void)
 	if (check_timer_margin(timer_margin))
 		return -EINVAL;
 
+	scu_ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(scu_ipc_dev))
+		return PTR_ERR(scu_ipc_dev);
+
 	watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
 
 	if (watchdog_device.timer_tbl_ptr == NULL) {
@@ -550,6 +553,7 @@ static void __exit intel_scu_watchdog_exit(void)
 {
 
 	misc_deregister(&watchdog_device.miscdev);
+	intel_ipc_dev_put(scu_ipc_dev);
 	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 	/* disable the timer */
 	iowrite32(0x00000002, watchdog_device.timer_control_addr);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [RFC v5 8/8] platform/x86: intel_scu_ipc: Use generic Intel IPC device calls
@ 2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan.kuppuswamy @ 2017-10-07 22:19 UTC (permalink / raw)
  To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
	dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
	sathyaosid, Kuppuswamy Sathyanarayanan

From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs.

This patch also cleans-up SCU IPC user drivers(rtc-mrst.c, intel-mid_wdt.c,
intel-mid_wdt.c, intel-mid.c) to use APIs provided by generic IPC driver.

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 arch/x86/include/asm/intel_scu_ipc.h    |  23 +-
 arch/x86/platform/intel-mid/intel-mid.c |  15 +-
 drivers/platform/x86/Kconfig            |   1 +
 drivers/platform/x86/intel_scu_ipc.c    | 483 +++++++++++++-------------------
 drivers/rtc/rtc-mrst.c                  |  16 +-
 drivers/watchdog/intel-mid_wdt.c        |  12 +-
 drivers/watchdog/intel_scu_watchdog.c   |  18 +-
 7 files changed, 256 insertions(+), 312 deletions(-)

Changes since v4:
 * None

Changes since v3:
 * Added intel_ipc_dev_put() support.

diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 81d3d87..5842534 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -14,9 +14,19 @@
 #define IPCMSG_COLD_BOOT	0xF3
 
 #define IPCMSG_VRTC		0xFA	 /* Set vRTC device */
-	/* Command id associated with message IPCMSG_VRTC */
-	#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
-	#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
+
+/* Command id associated with message IPCMSG_VRTC */
+#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
+#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
+
+#define INTEL_SCU_IPC_DEV	"intel_scu_ipc"
+#define SCU_PARAM_LEN		2
+
+static inline void scu_cmd_init(u32 *cmd, u32 param1, u32 param2)
+{
+	cmd[0] = param1;
+	cmd[1] = param2;
+}
 
 /* Read single register */
 int intel_scu_ipc_ioread8(u16 addr, u8 *data);
@@ -45,13 +55,6 @@ int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
 /* Update single register based on the mask */
 int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
 
-/* Issue commands to the SCU with or without data */
-int intel_scu_ipc_simple_command(int cmd, int sub);
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-			  u32 *out, int outlen);
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
-			      u32 *out, int outlen, u32 dptr, u32 sptr);
-
 /* I2C control api */
 int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
 
diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c
index 86676ce..23b3ab3 100644
--- a/arch/x86/platform/intel-mid/intel-mid.c
+++ b/arch/x86/platform/intel-mid/intel-mid.c
@@ -22,6 +22,7 @@
 #include <linux/irq.h>
 #include <linux/export.h>
 #include <linux/notifier.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -70,16 +71,26 @@ EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip);
 
 static void intel_mid_power_off(void)
 {
+	struct intel_ipc_dev *ipc_dev;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_OFF, 1};
+
 	/* Shut down South Complex via PWRMU */
 	intel_mid_pwr_power_off();
 
+	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
 	/* Only for Tangier, the rest will ignore this command */
-	intel_scu_ipc_simple_command(IPCMSG_COLD_OFF, 1);
+	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+	intel_ipc_dev_put(ipc_dev);
 };
 
 static void intel_mid_reboot(void)
 {
-	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
+	struct intel_ipc_dev *ipc_dev;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_BOOT, 0};
+
+	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+	intel_ipc_dev_put(ipc_dev);
 }
 
 static unsigned long __init intel_mid_calibrate_tsc(void)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 9442c23..1b9093c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -863,6 +863,7 @@ config INTEL_VBTN
 config INTEL_SCU_IPC
 	bool "Intel SCU IPC Support"
 	depends on X86_INTEL_MID
+	select REGMAP_MMIO
 	default y
 	---help---
 	  IPC is used to bridge the communications between kernel and SCU on
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 2c85f75..8812202 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -24,6 +24,8 @@
 #include <linux/pci.h>
 #include <linux/interrupt.h>
 #include <linux/sfi.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 #include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
@@ -39,6 +41,25 @@
 #define IPC_CMD_PCNTRL_R      1 /* Register read */
 #define IPC_CMD_PCNTRL_M      2 /* Register read-modify-write */
 
+/* IPC dev register offsets */
+/*
+ * IPC Read Buffer (Read Only):
+ * 16 byte buffer for receiving data from SCU, if IPC command
+ * processing results in response data
+ */
+#define IPC_DEV_SCU_RBUF_OFFSET			0x90
+#define IPC_DEV_SCU_WRBUF_OFFSET		0x80
+#define IPC_DEV_SCU_SPTR_OFFSET			0x08
+#define IPC_DEV_SCU_DPTR_OFFSET			0x0C
+#define IPC_DEV_SCU_STATUS_OFFSET		0x04
+
+/* IPC dev commands */
+/* IPC command register IOC bit */
+#define	IPC_DEV_SCU_CMD_MSI			BIT(8)
+#define	IPC_DEV_SCU_CMD_STATUS_ERR		BIT(1)
+#define	IPC_DEV_SCU_CMD_STATUS_ERR_MASK		GENMASK(7, 0)
+#define	IPC_DEV_SCU_CMD_STATUS_BUSY		BIT(0)
+
 /*
  * IPC register summary
  *
@@ -59,6 +80,11 @@
 #define IPC_WWBUF_SIZE    20		/* IPC Write buffer Size */
 #define IPC_RWBUF_SIZE    20		/* IPC Read buffer Size */
 #define IPC_IOC	          0x100		/* IPC command register IOC bit */
+#define	IPC_CMD_SIZE            16
+#define	IPC_CMD_SUBCMD          12
+#define	IPC_RWBUF_SIZE_DWORD    5
+#define	IPC_WWBUF_SIZE_DWORD    5
+
 
 #define PCI_DEVICE_ID_LINCROFT		0x082a
 #define PCI_DEVICE_ID_PENWELL		0x080e
@@ -93,140 +119,49 @@ static const struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
 
 struct intel_scu_ipc_dev {
 	struct device *dev;
+	struct intel_ipc_dev *ipc_dev;
 	void __iomem *ipc_base;
 	void __iomem *i2c_base;
-	struct completion cmd_complete;
+	struct regmap *ipc_regs;
+	struct regmap *i2c_regs;
 	u8 irq_mode;
 };
 
-static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
+static struct regmap_config ipc_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+};
 
-/*
- * IPC Read Buffer (Read Only):
- * 16 byte buffer for receiving data from SCU, if IPC command
- * processing results in response data
- */
-#define IPC_READ_BUFFER		0x90
+static struct regmap_config i2c_regmap_config = {
+        .reg_bits = 32,
+        .reg_stride = 4,
+        .val_bits = 32,
+	.fast_io = true,
+};
+
+static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
 
 #define IPC_I2C_CNTRL_ADDR	0
 #define I2C_DATA_ADDR		0x04
 
-static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
-
-/*
- * Send ipc command
- * Command Register (Write Only):
- * A write to this register results in an interrupt to the SCU core processor
- * Format:
- * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
- */
-static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd)
-{
-	if (scu->irq_mode) {
-		reinit_completion(&scu->cmd_complete);
-		writel(cmd | IPC_IOC, scu->ipc_base);
-	}
-	writel(cmd, scu->ipc_base);
-}
-
-/*
- * Write ipc data
- * IPC Write Buffer (Write Only):
- * 16-byte buffer for sending data associated with IPC command to
- * SCU. Size of the data is specified in the IPC_COMMAND_REG register
- */
-static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset)
-{
-	writel(data, scu->ipc_base + 0x80 + offset);
-}
-
-/*
- * Status Register (Read Only):
- * Driver will read this register to get the ready/busy status of the IPC
- * block and error status of the IPC command that was just processed by SCU
- * Format:
- * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
- */
-static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu)
-{
-	return __raw_readl(scu->ipc_base + 0x04);
-}
-
-/* Read ipc byte data */
-static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset)
-{
-	return readb(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Read ipc u32 data */
-static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset)
-{
-	return readl(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Wait till scu status is busy */
-static inline int busy_loop(struct intel_scu_ipc_dev *scu)
-{
-	u32 status = ipc_read_status(scu);
-	u32 loop_count = 100000;
-
-	/* break if scu doesn't reset busy bit after huge retry */
-	while ((status & BIT(0)) && --loop_count) {
-		udelay(1); /* scu processing time is in few u secods */
-		status = ipc_read_status(scu);
-	}
-
-	if (status & BIT(0)) {
-		dev_err(scu->dev, "IPC timed out");
-		return -ETIMEDOUT;
-	}
-
-	if (status & BIT(1))
-		return -EIO;
-
-	return 0;
-}
-
-/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
-static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
-{
-	int status;
-
-	if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
-		dev_err(scu->dev, "IPC timed out\n");
-		return -ETIMEDOUT;
-	}
-
-	status = ipc_read_status(scu);
-	if (status & BIT(1))
-		return -EIO;
-
-	return 0;
-}
-
-static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
-{
-	return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
-}
-
 /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
 static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 {
 	struct intel_scu_ipc_dev *scu = &ipcdev;
 	int nc;
 	u32 offset = 0;
-	int err;
+	int err = -EIO;
 	u8 cbuf[IPC_WWBUF_SIZE];
 	u32 *wbuf = (u32 *)&cbuf;
+	u32 cmd[SCU_PARAM_LEN] = {0};
+	/* max rbuf size is 20 bytes */
+	u8 rbuf[IPC_RWBUF_SIZE] = {0};
+	u32 rbuflen = DIV_ROUND_UP(count, 4);
 
 	memset(cbuf, 0, sizeof(cbuf));
 
-	mutex_lock(&ipclock);
-
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
+	scu_cmd_init(cmd, op, id);
 
 	for (nc = 0; nc < count; nc++, offset += 2) {
 		cbuf[offset] = addr[nc];
@@ -234,30 +169,30 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 	}
 
 	if (id == IPC_CMD_PCNTRL_R) {
-		for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
-			ipc_data_writel(scu, wbuf[nc], offset);
-		ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, count * 2, (u32 *)rbuf,
+				IPC_RWBUF_SIZE_DWORD, 0, 0);
 	} else if (id == IPC_CMD_PCNTRL_W) {
 		for (nc = 0; nc < count; nc++, offset += 1)
 			cbuf[offset] = data[nc];
-		for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
-			ipc_data_writel(scu, wbuf[nc], offset);
-		ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, count * 3, NULL, 0, 0, 0);
+
 	} else if (id == IPC_CMD_PCNTRL_M) {
 		cbuf[offset] = data[0];
 		cbuf[offset + 1] = data[1];
-		ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */
-		ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op);
+		err = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
+				(u8 *)wbuf, 4, NULL, 0, 0, 0);
 	}
 
-	err = intel_scu_ipc_check_status(scu);
 	if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
 		/* Workaround: values are read as 0 without memcpy_fromio */
 		memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16);
-		for (nc = 0; nc < count; nc++)
-			data[nc] = ipc_data_readb(scu, nc);
+		regmap_bulk_read(scu->ipc_regs, IPC_DEV_SCU_RBUF_OFFSET,
+					rbuf, rbuflen);
+		memcpy(data, rbuf, count);
 	}
-	mutex_unlock(&ipclock);
+
 	return err;
 }
 
@@ -422,138 +357,6 @@ int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
 }
 EXPORT_SYMBOL(intel_scu_ipc_update_register);
 
-/**
- *	intel_scu_ipc_simple_command	-	send a simple command
- *	@cmd: command
- *	@sub: sub type
- *
- *	Issue a simple command to the SCU. Do not use this interface if
- *	you must then access data as any data values may be overwritten
- *	by another SCU access by the time this function returns.
- *
- *	This function may sleep. Locking for SCU accesses is handled for
- *	the caller.
- */
-int intel_scu_ipc_simple_command(int cmd, int sub)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int err;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-	ipc_command(scu, sub << 12 | cmd);
-	err = intel_scu_ipc_check_status(scu);
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
-
-/**
- *	intel_scu_ipc_command	-	command with data
- *	@cmd: command
- *	@sub: sub type
- *	@in: input data
- *	@inlen: input length in dwords
- *	@out: output data
- *	@outlein: output length in dwords
- *
- *	Issue a command to the SCU which involves data transfers. Do the
- *	data copies under the lock but leave it for the caller to interpret
- */
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-			  u32 *out, int outlen)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int i, err;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-
-	for (i = 0; i < inlen; i++)
-		ipc_data_writel(scu, *in++, 4 * i);
-
-	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
-	err = intel_scu_ipc_check_status(scu);
-
-	if (!err) {
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(scu, 4 * i);
-	}
-
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_command);
-
-#define IPC_SPTR		0x08
-#define IPC_DPTR		0x0C
-
-/**
- * intel_scu_ipc_raw_command() - IPC command with data and pointers
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in dwords.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- * @sptr:	data writing to SPTR register.
- * @dptr:	data writing to DPTR register.
- *
- * Send an IPC command to SCU with input/output data and source/dest pointers.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
-			      u32 *out, int outlen, u32 dptr, u32 sptr)
-{
-	struct intel_scu_ipc_dev *scu = &ipcdev;
-	int inbuflen = DIV_ROUND_UP(inlen, 4);
-	u32 inbuf[4];
-	int i, err;
-
-	/* Up to 16 bytes */
-	if (inbuflen > 4)
-		return -EINVAL;
-
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
-
-	writel(dptr, scu->ipc_base + IPC_DPTR);
-	writel(sptr, scu->ipc_base + IPC_SPTR);
-
-	/*
-	 * SRAM controller doesn't support 8-bit writes, it only
-	 * supports 32-bit writes, so we have to copy input data into
-	 * the temporary buffer, and SCU FW will use the inlen to
-	 * determine the actual input data length in the temporary
-	 * buffer.
-	 */
-	memcpy(inbuf, in, inlen);
-
-	for (i = 0; i < inbuflen; i++)
-		ipc_data_writel(scu, inbuf[i], 4 * i);
-
-	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
-	err = intel_scu_ipc_check_status(scu);
-	if (!err) {
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(scu, 4 * i);
-	}
-
-	mutex_unlock(&ipclock);
-	return err;
-}
-EXPORT_SYMBOL_GPL(intel_scu_ipc_raw_command);
-
 /* I2C commands */
 #define IPC_I2C_WRITE 1 /* I2C Write command */
 #define IPC_I2C_READ  2 /* I2C Read command */
@@ -575,48 +378,143 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
 	struct intel_scu_ipc_dev *scu = &ipcdev;
 	u32 cmd = 0;
 
-	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
-		mutex_unlock(&ipclock);
-		return -ENODEV;
-	}
 	cmd = (addr >> 24) & 0xFF;
 	if (cmd == IPC_I2C_READ) {
-		writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+		regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
 		/* Write not getting updated without delay */
 		mdelay(1);
-		*data = readl(scu->i2c_base + I2C_DATA_ADDR);
+		regmap_read(scu->i2c_regs, I2C_DATA_ADDR, data);
 	} else if (cmd == IPC_I2C_WRITE) {
-		writel(*data, scu->i2c_base + I2C_DATA_ADDR);
+		regmap_write(scu->i2c_regs, I2C_DATA_ADDR, *data);
 		mdelay(1);
-		writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+		regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
 	} else {
 		dev_err(scu->dev,
 			"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
 
-		mutex_unlock(&ipclock);
 		return -EIO;
 	}
-	mutex_unlock(&ipclock);
 	return 0;
 }
 EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
 
-/*
- * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
- * When ioc bit is set to 1, caller api must wait for interrupt handler called
- * which in turn unlocks the caller api. Currently this is not used
- *
- * This is edge triggered so we need take no action to clear anything
- */
-static irqreturn_t ioc(int irq, void *dev_id)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
 {
-	struct intel_scu_ipc_dev *scu = dev_id;
+	if (!cmd_list || cmdlen != SCU_PARAM_LEN)
+		return -EINVAL;
 
-	if (scu->irq_mode)
-		complete(&scu->cmd_complete);
+	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
 
-	return IRQ_HANDLED;
+	return 0;
+}
+
+static int pre_cmd_fn(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
+		u32 *out, u32 outlen)
+{
+	int ret;
+
+	if (inlen > IPC_WWBUF_SIZE_DWORD || outlen > IPC_RWBUF_SIZE_DWORD)
+		return -EINVAL;
+
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
+
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
+
+	return 0;
+}
+
+static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
+		u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	int ret;
+
+	if (inlen > IPC_WWBUF_SIZE || outlen > IPC_RWBUF_SIZE_DWORD)
+		return -EINVAL;
+
+	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
+	if (ret < 0)
+		return ret;
+
+	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
+
+	return 0;
+}
+
+static int scu_ipc_err_code(int status)
+{
+	if (status & IPC_DEV_SCU_CMD_STATUS_ERR)
+		return (status & IPC_DEV_SCU_CMD_STATUS_ERR_MASK);
+	else
+		return 0;
+}
+
+static int scu_ipc_busy_check(int status)
+{
+	return status | IPC_DEV_SCU_CMD_STATUS_BUSY;
+}
+
+static u32 scu_ipc_enable_msi(u32 cmd)
+{
+	return cmd | IPC_DEV_SCU_CMD_MSI;
+}
+
+static struct intel_ipc_dev *intel_scu_ipc_dev_create(
+		struct device *dev,
+		void __iomem *base,
+		int irq)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+	struct regmap *ipc_regs;
+	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
+
+	cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+        ipc_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
+			&ipc_regmap_config);
+        if (IS_ERR(ipc_regs)) {
+                dev_err(dev, "ipc_regs regmap init failed\n");
+                return ERR_CAST(ipc_regs);;
+        }
+
+	scu->ipc_regs = ipc_regs;
+
+	/* set IPC dev ops */
+	ops->to_err_code = scu_ipc_err_code;
+	ops->busy_check = scu_ipc_busy_check;
+	ops->enable_msi = scu_ipc_enable_msi;
+	ops->pre_cmd_fn = pre_cmd_fn;
+	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+
+	/* set cfg options */
+	if (scu->irq_mode)
+		cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+		cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_SCU;
+	cfg->irq = irq;
+	cfg->use_msi = true;
+	cfg->support_sptr = true;
+	cfg->support_dptr = true;
+	cfg->cmd_regs = ipc_regs;
+	cfg->data_regs = ipc_regs;
+	cfg->wrbuf_reg = IPC_DEV_SCU_WRBUF_OFFSET;
+	cfg->rbuf_reg = IPC_DEV_SCU_RBUF_OFFSET;
+	cfg->sptr_reg = IPC_DEV_SCU_SPTR_OFFSET;
+	cfg->dptr_reg = IPC_DEV_SCU_DPTR_OFFSET;
+	cfg->status_reg = IPC_DEV_SCU_STATUS_OFFSET;
+
+	return devm_intel_ipc_dev_create(dev, INTEL_SCU_IPC_DEV, cfg, ops);
 }
 
 /**
@@ -650,25 +548,34 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		return err;
 
-	init_completion(&scu->cmd_complete);
-
 	scu->ipc_base = pcim_iomap_table(pdev)[0];
 
-	scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
+	scu->i2c_base = devm_ioremap_nocache(&pdev->dev, pdata->i2c_base,
+			pdata->i2c_len);
 	if (!scu->i2c_base)
 		return -ENOMEM;
 
-	err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
-			       scu);
-	if (err)
-		return err;
+	pci_set_drvdata(pdev, scu);
+
+        scu->i2c_regs = devm_regmap_init_mmio_clk(&pdev->dev, NULL,
+			scu->i2c_base, &i2c_regmap_config);
+        if (IS_ERR(scu->i2c_regs)) {
+                dev_err(&pdev->dev, "i2c_regs regmap init failed\n");
+                return PTR_ERR(scu->i2c_regs);;
+        }
+
+	scu->ipc_dev = intel_scu_ipc_dev_create(&pdev->dev, scu->ipc_base,
+			pdev->irq);
+	if (IS_ERR(scu->ipc_dev)) {
+		dev_err(&pdev->dev, "Failed to create SCU IPC device\n");
+		return PTR_ERR(scu->ipc_dev);
+	}
 
 	/* Assign device at last */
 	scu->dev = &pdev->dev;
 
 	intel_scu_devices_create();
 
-	pci_set_drvdata(pdev, scu);
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index 7334c44..37595c4 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -36,6 +36,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/sfi.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/intel_scu_ipc.h>
 #include <asm/intel-mid.h>
@@ -46,6 +47,7 @@ struct mrst_rtc {
 	struct device		*dev;
 	int			irq;
 	struct resource		*iomem;
+	struct intel_ipc_dev	*ipc_dev;
 
 	u8			enabled_wake;
 	u8			suspend_ctrl;
@@ -110,10 +112,11 @@ static int mrst_read_time(struct device *dev, struct rtc_time *time)
 
 static int mrst_set_time(struct device *dev, struct rtc_time *time)
 {
-	int ret;
 	unsigned long flags;
 	unsigned char mon, day, hrs, min, sec;
 	unsigned int yrs;
+	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME};
 
 	yrs = time->tm_year;
 	mon = time->tm_mon + 1;   /* tm_mon starts at zero */
@@ -137,8 +140,7 @@ static int mrst_set_time(struct device *dev, struct rtc_time *time)
 
 	spin_unlock_irqrestore(&rtc_lock, flags);
 
-	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
-	return ret;
+	return ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
 }
 
 static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
@@ -210,6 +212,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 	struct mrst_rtc	*mrst = dev_get_drvdata(dev);
 	unsigned char hrs, min, sec;
 	int ret = 0;
+	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM};
 
 	if (!mrst->irq)
 		return -EIO;
@@ -229,7 +232,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 
 	spin_unlock_irq(&rtc_lock);
 
-	ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
+	ret = ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
 	if (ret)
 		return ret;
 
@@ -329,6 +332,10 @@ static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
 	if (!iomem)
 		return -ENODEV;
 
+	mrst_rtc.ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(mrst_rtc.ipc_dev))
+		return PTR_ERR(mrst_rtc.ipc_dev);
+
 	iomem = request_mem_region(iomem->start, resource_size(iomem),
 				   driver_name);
 	if (!iomem) {
@@ -394,6 +401,7 @@ static void rtc_mrst_do_remove(struct device *dev)
 
 	rtc_mrst_do_shutdown();
 
+	intel_ipc_dev_put(mrst->ipc_dev);
 	if (mrst->irq)
 		free_irq(mrst->irq, mrst->rtc);
 
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 72c108a..0d455bd 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -18,6 +18,7 @@
 #include <linux/platform_device.h>
 #include <linux/watchdog.h>
 #include <linux/platform_data/intel-mid_wdt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include <asm/intel_scu_ipc.h>
 #include <asm/intel-mid.h>
@@ -29,6 +30,8 @@
 #define MID_WDT_TIMEOUT_MAX		170
 #define MID_WDT_DEFAULT_TIMEOUT		90
 
+static struct intel_ipc_dev *scu_ipc_dev;
+
 /* SCU watchdog messages */
 enum {
 	SCU_WATCHDOG_START = 0,
@@ -38,7 +41,10 @@ enum {
 
 static inline int wdt_command(int sub, u32 *in, int inlen)
 {
-	return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+	u32 cmds[SCU_PARAM_LEN] = {IPC_WATCHDOG, sub};
+
+	return ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, in,
+			inlen, NULL, 0);
 }
 
 static int wdt_start(struct watchdog_device *wd)
@@ -129,6 +135,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	if (!wdt_dev)
 		return -ENOMEM;
 
+	scu_ipc_dev = devm_intel_ipc_dev_get(&pdev->dev, INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(scu_ipc_dev))
+		return PTR_ERR(scu_ipc_dev);
+
 	wdt_dev->info = &mid_wdt_info;
 	wdt_dev->ops = &mid_wdt_ops;
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index 0caab62..55169cb 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -49,6 +49,7 @@
 #include <asm/intel_scu_ipc.h>
 #include <asm/apb_timer.h>
 #include <asm/intel-mid.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
 
 #include "intel_scu_watchdog.h"
 
@@ -94,6 +95,8 @@ MODULE_PARM_DESC(force_boot,
 
 static struct intel_scu_watchdog_dev watchdog_device;
 
+static struct intel_ipc_dev *scu_ipc_dev;
+
 /* Forces restart, if force_reboot is set */
 static void watchdog_fire(void)
 {
@@ -128,18 +131,14 @@ static int watchdog_set_ipc(int soft_threshold, int threshold)
 	u32	*ipc_wbuf;
 	u8	 cbuf[16] = { '\0' };
 	int	 ipc_ret = 0;
+	u32 cmds[SCU_PARAM_LEN] = {IPC_SET_WATCHDOG_TIMER, 0};
 
 	ipc_wbuf = (u32 *)&cbuf;
 	ipc_wbuf[0] = soft_threshold;
 	ipc_wbuf[1] = threshold;
 
-	ipc_ret = intel_scu_ipc_command(
-			IPC_SET_WATCHDOG_TIMER,
-			0,
-			ipc_wbuf,
-			2,
-			NULL,
-			0);
+	ipc_ret = ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, ipc_wbuf,
+			2, NULL, 0);
 
 	if (ipc_ret != 0)
 		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
@@ -460,6 +459,10 @@ static int __init intel_scu_watchdog_init(void)
 	if (check_timer_margin(timer_margin))
 		return -EINVAL;
 
+	scu_ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+	if (IS_ERR_OR_NULL(scu_ipc_dev))
+		return PTR_ERR(scu_ipc_dev);
+
 	watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
 
 	if (watchdog_device.timer_tbl_ptr == NULL) {
@@ -550,6 +553,7 @@ static void __exit intel_scu_watchdog_exit(void)
 {
 
 	misc_deregister(&watchdog_device.miscdev);
+	intel_ipc_dev_put(scu_ipc_dev);
 	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 	/* disable the timer */
 	iowrite32(0x00000002, watchdog_device.timer_control_addr);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  (?)
@ 2017-10-08 18:38     ` Andy Shevchenko
  -1 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2017-10-08 18:38 UTC (permalink / raw)
  To: Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86, Wim Van Sebroeck, Ingo Molnar,
	Alexandre Belloni, Zha Qipeng, H. Peter Anvin, dvhart,
	Thomas Gleixner, Lee Jones, Andy Shevchenko,
	Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, Platform Driver,
	Sathyanarayanan Kuppuswamy Natarajan, Kuppuswamy Sathyanarayanan

On Sun, Oct 8, 2017 at 1:19 AM,
<sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>
> Currently, update_no_reboot_bit() function implemented in this driver
> uses mutex_lock() to protect its register updates. But this function is
> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
> atomic context" issue. This patch fixes this issue by replacing the
> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>
> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>

>  * Rebased this patch on top of Andy's review branch.

Oh, what I asked you is to use vanilla kernel as a base.
Please, be sure (you assured me, though it's not true) that it's
applied against vanilla (or our fixes branch) and send just this one
patch separately.

No need to resend v5 right now.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
@ 2017-10-08 18:38     ` Andy Shevchenko
  0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2017-10-08 18:38 UTC (permalink / raw)
  To: Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86, Wim Van Sebroeck, Ingo Molnar,
	Alexandre Belloni, Zha Qipeng, H. Peter Anvin, dvhart,
	Thomas Gleixner, Lee Jones, Andy Shevchenko,
	Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, Platform Driver,
	Sathyanarayanan Kuppuswamy Natarajan, Kuppuswamy Sathyanarayanan

On Sun, Oct 8, 2017 at 1:19 AM,
<sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>
> Currently, update_no_reboot_bit() function implemented in this driver
> uses mutex_lock() to protect its register updates. But this function is
> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
> atomic context" issue. This patch fixes this issue by replacing the
> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>
> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>

>  * Rebased this patch on top of Andy's review branch.

Oh, what I asked you is to use vanilla kernel as a base.
Please, be sure (you assured me, though it's not true) that it's
applied against vanilla (or our fixes branch) and send just this one
patch separately.

No need to resend v5 right now.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
@ 2017-10-08 18:38     ` Andy Shevchenko
  0 siblings, 0 replies; 53+ messages in thread
From: Andy Shevchenko @ 2017-10-08 18:38 UTC (permalink / raw)
  To: Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86-DgEjT+Ai2ygdnm+yROfE0A, Wim Van Sebroeck,
	Ingo Molnar, Alexandre Belloni, Zha Qipeng, H. Peter Anvin,
	dvhart-wEGCiKHe2LqWVfeAwA7xHQ, Thomas Gleixner, Lee Jones,
	Andy Shevchenko, Souvik Kumar Chakravarty,
	linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Platform Driver,
	Sathyanarayanan Kuppuswamy Natarajan, Kuppuswamy Sathyanarayanan

On Sun, Oct 8, 2017 at 1:19 AM,
<sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
>
> Currently, update_no_reboot_bit() function implemented in this driver
> uses mutex_lock() to protect its register updates. But this function is
> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
> atomic context" issue. This patch fixes this issue by replacing the
> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>
> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

>  * Rebased this patch on top of Andy's review branch.

Oh, what I asked you is to use vanilla kernel as a base.
Please, be sure (you assured me, though it's not true) that it's
applied against vanilla (or our fixes branch) and send just this one
patch separately.

No need to resend v5 right now.

-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
  2017-10-08 18:38     ` Andy Shevchenko
  (?)
@ 2017-10-08 18:54       ` Kuppuswamy, Sathyanarayanan
  -1 siblings, 0 replies; 53+ messages in thread
From: Kuppuswamy, Sathyanarayanan @ 2017-10-08 18:54 UTC (permalink / raw)
  To: Andy Shevchenko, Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86, Wim Van Sebroeck, Ingo Molnar,
	Alexandre Belloni, Zha Qipeng, H. Peter Anvin, dvhart,
	Thomas Gleixner, Lee Jones, Andy Shevchenko,
	Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, Platform Driver, Kuppuswamy Sathyanarayanan

Hi Andy,


On 10/8/2017 11:38 AM, Andy Shevchenko wrote:
> On Sun, Oct 8, 2017 at 1:19 AM,
> <sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
>> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Currently, update_no_reboot_bit() function implemented in this driver
>> uses mutex_lock() to protect its register updates. But this function is
>> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
>> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
>> atomic context" issue. This patch fixes this issue by replacing the
>> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>>
>> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
>> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>
>>   * Rebased this patch on top of Andy's review branch.
> Oh, what I asked you is to use vanilla kernel as a base.
> Please, be sure (you assured me, though it's not true)
I did test this patch on top of 4.14-rc3, but I have included another 
patch (""platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe 
function") from your review branch before testing.
I assumed that your will be pushing this patch along with devm_* fixes 
patch (since you already reviewed it), So I re-based them together on 
top of 4.14-rc3.  Sorry, it looks like my assumption is incorrect.
>   that it's
> applied against vanilla (or our fixes branch) and send just this one
> patch separately.
I will send it separately now.
>
> No need to resend v5 right now.
>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
@ 2017-10-08 18:54       ` Kuppuswamy, Sathyanarayanan
  0 siblings, 0 replies; 53+ messages in thread
From: Kuppuswamy, Sathyanarayanan @ 2017-10-08 18:54 UTC (permalink / raw)
  To: Andy Shevchenko, Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86, Wim Van Sebroeck, Ingo Molnar,
	Alexandre Belloni, Zha Qipeng, H. Peter Anvin, dvhart,
	Thomas Gleixner, Lee Jones, Andy Shevchenko,
	Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, Platform Driver, Kuppuswamy Sathyanarayanan

Hi Andy,


On 10/8/2017 11:38 AM, Andy Shevchenko wrote:
> On Sun, Oct 8, 2017 at 1:19 AM,
> <sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
>> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Currently, update_no_reboot_bit() function implemented in this driver
>> uses mutex_lock() to protect its register updates. But this function is
>> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
>> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
>> atomic context" issue. This patch fixes this issue by replacing the
>> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>>
>> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
>> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy@linux.intel.com>
>>   * Rebased this patch on top of Andy's review branch.
> Oh, what I asked you is to use vanilla kernel as a base.
> Please, be sure (you assured me, though it's not true)
I did test this patch on top of 4.14-rc3, but I have included another 
patch (""platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe 
function") from your review branch before testing.
I assumed that your will be pushing this patch along with devm_* fixes 
patch (since you already reviewed it), So I re-based them together on 
top of 4.14-rc3.  Sorry, it looks like my assumption is incorrect.
>   that it's
> applied against vanilla (or our fixes branch) and send just this one
> patch separately.
I will send it separately now.
>
> No need to resend v5 right now.
>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates
@ 2017-10-08 18:54       ` Kuppuswamy, Sathyanarayanan
  0 siblings, 0 replies; 53+ messages in thread
From: Kuppuswamy, Sathyanarayanan @ 2017-10-08 18:54 UTC (permalink / raw)
  To: Andy Shevchenko, Kuppuswamy Sathyanarayanan
  Cc: Alessandro Zummo, x86-DgEjT+Ai2ygdnm+yROfE0A, Wim Van Sebroeck,
	Ingo Molnar, Alexandre Belloni, Zha Qipeng, H. Peter Anvin,
	dvhart-wEGCiKHe2LqWVfeAwA7xHQ, Thomas Gleixner, Lee Jones,
	Andy Shevchenko, Souvik Kumar Chakravarty,
	linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Platform Driver,
	Kuppuswamy Sathyanarayanan

Hi Andy,


On 10/8/2017 11:38 AM, Andy Shevchenko wrote:
> On Sun, Oct 8, 2017 at 1:19 AM,
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> wrote:
>> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
>>
>> Currently, update_no_reboot_bit() function implemented in this driver
>> uses mutex_lock() to protect its register updates. But this function is
>> called with in atomic context in iTCO_wdt_start() and iTCO_wdt_stop()
>> functions in iTCO_wdt.c driver, which in turn causes "sleeping into
>> atomic context" issue. This patch fixes this issue by replacing the
>> mutex_lock() with spin_lock() to protect the GCR read/write/update APIs.
>>
>> Fixes: 9d855d4 ("platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure")
>> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kupuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
>>   * Rebased this patch on top of Andy's review branch.
> Oh, what I asked you is to use vanilla kernel as a base.
> Please, be sure (you assured me, though it's not true)
I did test this patch on top of 4.14-rc3, but I have included another 
patch (""platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe 
function") from your review branch before testing.
I assumed that your will be pushing this patch along with devm_* fixes 
patch (since you already reviewed it), So I re-based them together on 
top of 4.14-rc3.  Sorry, it looks like my assumption is incorrect.
>   that it's
> applied against vanilla (or our fixes branch) and send just this one
> patch separately.
I will send it separately now.
>
> No need to resend v5 right now.
>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  (?)
@ 2017-10-09  4:53     ` Chakravarty, Souvik K
  -1 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  4:53 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c redundantly
> implements the same IPC features and has lot of code duplication between
> them. This driver addresses this issue by grouping the common IPC
> functionalities under the same driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  drivers/platform/x86/Kconfig                    |   8 +
>  drivers/platform/x86/Makefile                   |   1 +
>  drivers/platform/x86/intel_ipc_dev.c            | 576
> ++++++++++++++++++++++++
>  include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
>  4 files changed, 791 insertions(+)
>  create mode 100644 drivers/platform/x86/intel_ipc_dev.c
>  create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Fixed NULL pointer exception in intel_ipc_dev_get().
>  * Fixed error in check for duplicate intel_ipc_dev.
>  * Added custom interrupt handler support.
>  * Used char array for error string conversion.
>  * Added put dev support.
>  * Added devm_* variant of intel_ipc_dev_get().
> 
> Changes since v2:
>  * Added ipc_dev_cmd API support.
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index da2d9ba..724ee696 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
>  	  with the OS-image for the device. This option supplies the missing
>  	  information. Enable this for x86 tablets with Silead touchscreens.
> 
> +config INTEL_IPC_DEV
> +	bool "Intel IPC Device Driver"
> +	depends on X86_64
> +	---help---
> +	  This driver implements core features of Intel IPC device. Devices
> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
> +	  driver to implement IPC protocol of their respective device.
> +
>  endif # X86_PLATFORM_DEVICES
> 
>  config PMC_ATOM
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 2b315d0..99a1af1 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
> pmc_atom.o
>  obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
>  obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
>  obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
> diff --git a/drivers/platform/x86/intel_ipc_dev.c
> b/drivers/platform/x86/intel_ipc_dev.c
> new file mode 100644
> index 0000000..f55ddec
> --- /dev/null
> +++ b/drivers/platform/x86/intel_ipc_dev.c
> @@ -0,0 +1,576 @@
> +/*
> + * intel_ipc_dev.c: Intel IPC device class driver
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +#include <linux/regmap.h>
> +
> +/* mutex to sync different ipc devices in same channel */ static struct
> +mutex channel_lock[IPC_CHANNEL_MAX];
> +
> +static char *ipc_err_sources[] = {
> +	[IPC_DEV_ERR_NONE] =
> +		"No error",
> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
> +		"Command not-supported/Invalid",
> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
> +		"Command not-serviced/Invalid param",
> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
> +		"Unable-to-service/Cmd-timeout",
> +	[IPC_DEV_ERR_CMD_INVALID] =
> +		"Command-invalid/Cmd-locked",
> +	[IPC_DEV_ERR_CMD_FAILED] =
> +		"Command-failed/Invalid-VR-id",
> +	[IPC_DEV_ERR_EMSECURITY] =
> +		"Invalid Battery/VR-Error",
> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
> +		"Unsigned kernel",
> +};
> +
> +static void ipc_channel_lock_init(void) {
> +	int i;
> +
> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
> +		mutex_init(&channel_lock[i]);
> +}
> +
> +static struct class intel_ipc_class = {
> +	.name = "intel_ipc",
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* acquire channel lock */
> +	mutex_lock(&channel_lock[chan_type]);
> +
> +	/* acquire IPC device lock */
> +	mutex_lock(&ipc_dev->lock);
> +
> +	return 0;
> +}
> +
> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* release IPC device lock */
> +	mutex_unlock(&ipc_dev->lock);
> +
> +	/* release channel lock */
> +	mutex_unlock(&channel_lock[chan_type]);
> +
> +	return 0;
> +}
> +
> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
> +	int error)
> +{
> +	if (error < IPC_DEV_ERR_MAX)
> +		return ipc_err_sources[error];
> +
> +	return "Unknown Command";
> +}
> +
> +/* Helper function to send given command to IPC device */ static inline
> +void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
> +	ipc_dev->cmd = cmd;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
> +		reinit_completion(&ipc_dev->cmd_complete);
> +
> +	if (ipc_dev->ops->enable_msi)
> +		cmd = ipc_dev->ops->enable_msi(cmd);
> +
> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
> }
> +
> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
> +	int status;
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->busy_check)
> +		return ipc_dev->ops->busy_check(status);
> +
> +	return 0;
> +}
> +
> +/* Check the status of IPC command and return err code if failed */
> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
> +	int status;
> +	int ret = 0;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
> +				IPC_DEV_CMD_TIMEOUT))
> +			ret = -ETIMEDOUT;
> +	} else {
> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
> +			udelay(1);
> +		if (!loop_count)
> +			ret = -ETIMEDOUT;
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
> >cmd);
> +		return ret;
> +	}
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->to_err_code)
> +		ret = ipc_dev->ops->to_err_code(status);
> +
> +	if (ret) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> +				ipc_dev_err_string(ipc_dev, ret),
> +				status, ipc_dev->cmd);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * ipc_dev_simple_cmd() - Send simple IPC command
> + * @ipc_dev     : Reference to ipc device.
> + * @cmd_list    : IPC command list.
> + * @cmdlen      : Number of cmd/sub-cmds.
> + *
> + * Send a simple IPC command to ipc device.
> + * Use this when don't need to specify input/output data
> + * and source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen)
> +{
> +	int ret;
> +
> +	if (!cmd_list)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler */
> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
> +
> +/**
> + * ipc_dev_cmd() - Send IPC command with data.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in dwords.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + *
> + * Send an IPC command to device with input/output data.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	int ret;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_cmd_fn) {
> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
> +				out, outlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
> +
> +/**
> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in bytes.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + * @dptr        : IPC destination data address.
> + * @sptr        : IPC source data address.
> + *
> + * Send an IPC command to device with input/output data and
> + * source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */

Sorry for coming in so late but since we are refactoring the API anyways, isn't it better to reduce the signature? Nine parameters is an awful lot and prone to errors and difficult to debug. (We found it out the hard way when we started using this a few years ago.)

This can be consolidated into a few neat structs, e.g.,:
int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd, 
		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)

Same for the ipc_dev_cmd() APIs above as well.

> +
> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
> +	int ret;
> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
> +	u32 *inbuf;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
> +	if (!inbuf)
> +		return -ENOMEM;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
> inlen,
> +				out, outlen, dptr, sptr);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* If supported, write DPTR register.*/
> +	if (ipc_dev->cfg->support_dptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >dptr_reg,
> +				dptr);
> +
> +	/* If supported, write SPTR register. */
> +	if (ipc_dev->cfg->support_sptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >sptr_reg,
> +				sptr);
> +
> +	memcpy(inbuf, in, inlen);
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +	kfree(inbuf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
> +
> +/* sysfs option to send simple IPC commands from userspace */ static
> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t count) {
> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
> +	u32 cmd;
> +	int ret;
> +
> +	ret = sscanf(buf, "%d", &cmd);
> +	if (ret != 1) {
> +		dev_err(dev, "Error args\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
> +	if (ret) {
> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
> +		return ret;
> +	}
> +	return (ssize_t)count;
> +}
> +
> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
> +
> +static struct attribute *ipc_dev_attrs[] = {
> +	&dev_attr_send_cmd.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group ipc_dev_group = {
> +	.attrs = ipc_dev_attrs,
> +};
> +
> +static const struct attribute_group *ipc_dev_groups[] = {
> +	&ipc_dev_group,
> +	NULL,
> +};
> +
> +/* IPC device IRQ handler */
> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
> +
> +	if (ipc_dev->ops->pre_irq_handler_fn)
> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
> +
> +	complete(&ipc_dev->cmd_complete);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
> +
> +	if (!ipc_dev)
> +		return;
> +
> +	device_del(&ipc_dev->dev);
> +
> +	kfree(ipc_dev);
> +}
> +
> +static int match_name(struct device *dev, const void *data) {
> +        if (!dev_name(dev))
> +                return 0;
> +
> +        return !strcmp(dev_name(dev), (char *)data); }
> +
> +/**
> + * intel_ipc_dev_get() - Get Intel IPC device from name.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
> +        struct device *dev;
> +
> +	if (!dev_name)
> +		return ERR_PTR(-EINVAL);
> +
> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
> match_name);
> +
> +	return dev ? dev_get_drvdata(dev) : NULL; }
> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
> +
> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
> +
> +/**
> + * devm_intel_ipc_dev_get() - Resource managed version of
> intel_ipc_dev_get().
> + * @dev         : Device pointer.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
> GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = intel_ipc_dev_get(dev_name);
> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
> +		*ptr = ipc_dev;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return ipc_dev;
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
> +
> +/**
> + * devm_intel_ipc_dev_create() - Create IPC device
> + * @dev		: IPC parent device.
> + * @devname	: Name of the IPC device.
> + * @cfg		: IPC device configuration.
> + * @ops		: IPC device ops.
> + *
> + * Resource managed API to create IPC device with
> + * given configuration.
> + *
> + * Return	: IPC device pointer or ERR_PTR(error code).
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname,
> +		struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +	int ret;
> +
> +	if (!dev && !devname && !cfg)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (intel_ipc_dev_get(devname)) {
> +		dev_err(dev, "IPC device %s already exist\n", devname);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
> +			GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
> +	if (!ipc_dev) {
> +		ret = -ENOMEM;
> +		goto err_dev_create;
> +	}
> +
> +	ipc_dev->dev.class = &intel_ipc_class;
> +	ipc_dev->dev.parent = dev;
> +	ipc_dev->dev.groups = ipc_dev_groups;
> +	ipc_dev->cfg = cfg;
> +	ipc_dev->ops = ops;
> +
> +	mutex_init(&ipc_dev->lock);
> +	init_completion(&ipc_dev->cmd_complete);
> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
> +	dev_set_name(&ipc_dev->dev, devname);
> +	device_initialize(&ipc_dev->dev);
> +
> +	ret = device_add(&ipc_dev->dev);
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
> +				__func__);
> +		ret = -ENODEV;
> +		goto err_dev_add;
> +	}
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (devm_request_irq(&ipc_dev->dev,
> +				ipc_dev->cfg->irq,
> +				ipc_dev_irq_handler,
> +				ipc_dev->cfg->irqflags,
> +				dev_name(&ipc_dev->dev),
> +				ipc_dev)) {
> +			dev_err(&ipc_dev->dev,
> +					"Failed to request irq\n");
> +			goto err_irq_request;
> +		}
> +	}
> +
> +	*ptr = ipc_dev;
> +
> +	devres_add(dev, ptr);
> +
> +	return ipc_dev;
> +
> +err_irq_request:
> +	device_del(&ipc_dev->dev);
> +err_dev_add:
> +	kfree(ipc_dev);
> +err_dev_create:
> +	devres_free(ptr);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
> +
> +static int __init intel_ipc_init(void)
> +{
> +	ipc_channel_lock_init();
> +	return class_register(&intel_ipc_class); }
> +
> +static void __exit intel_ipc_exit(void) {
> +	class_unregister(&intel_ipc_class);
> +}
> +subsys_initcall(intel_ipc_init);
> +module_exit(intel_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Kuppuswamy
> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
> +MODULE_DESCRIPTION("Intel IPC device class driver");
> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
> b/include/linux/platform_data/x86/intel_ipc_dev.h
> new file mode 100644
> index 0000000..eaeedaf
> --- /dev/null
> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
> @@ -0,0 +1,206 @@
> +/*
> + * Intel IPC class device header file.
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#ifndef INTEL_IPC_DEV_H
> +#define INTEL_IPC_DEV_H
> +
> +#include <linux/module.h>
> +#include <linux/device.h>
> +
> +/* IPC channel type */
> +#define IPC_CHANNEL_IA_PMC                      0
> +#define IPC_CHANNEL_IA_PUNIT                    1
> +#define IPC_CHANNEL_PMC_PUNIT                   2
> +#define IPC_CHANNEL_IA_SCU                      3
> +#define IPC_CHANNEL_MAX                         4
> +
> +/* IPC return code */
> +#define IPC_DEV_ERR_NONE			0
> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
> +#define IPC_DEV_ERR_CMD_INVALID			4
> +#define IPC_DEV_ERR_CMD_FAILED			5
> +#define IPC_DEV_ERR_EMSECURITY			6
> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
> +#define IPC_DEV_ERR_MAX				8
> +
> +/* IPC mode */
> +#define IPC_DEV_MODE_IRQ			0
> +#define IPC_DEV_MODE_POLLING			1
> +
> +/* IPC dev constants */
> +#define IPC_DEV_CMD_LOOP_CNT			3000000
> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
> +#define IPC_DEV_DATA_BUFFER_SIZE		16
> +
> +struct intel_ipc_dev;
> +struct intel_ipc_raw_cmd;
> +
> +/**
> + * struct intel_ipc_dev_cfg - IPC device config structure.
> + *
> + * IPC device drivers uses the following config options to
> + * register new IPC device.
> + *
> + * @cmd_regs            : IPC device command base regmap.
> + * @data_regs           : IPC device data base regmap.
> + * @wrbuf_reg           : IPC device data write register address.
> + * @rbuf_reg            : IPC device data read register address.
> + * @sptr_reg            : IPC device source data pointer register address.
> + * @dptr_reg            : IPC device destination data pointer register
> + *                        address.
> + * @status_reg          : IPC command status register address.
> + * @cmd_reg             : IPC command register address.
> + * @mode                : IRQ/POLLING mode.
> + * @irq                 : IPC device IRQ number.
> + * @irqflags            : IPC device IRQ flags.
> + * @chan_type           : IPC device channel type(PMC/PUNIT).
> + * @msi                 : Enable/Disable MSI for IPC commands.
> + * @support_dptr        : Support DPTR update.
> + * @support_sptr        : Support SPTR update.
> + *
> + */
> +struct intel_ipc_dev_cfg {
> +	struct regmap *cmd_regs;
> +	struct regmap *data_regs;
> +	unsigned int wrbuf_reg;
> +	unsigned int rbuf_reg;
> +	unsigned int sptr_reg;
> +	unsigned int dptr_reg;
> +	unsigned int status_reg;
> +	unsigned int cmd_reg;
> +	int mode;
> +	int irq;
> +	int irqflags;
> +	int chan_type;
> +	bool use_msi;
> +	bool support_dptr;
> +	bool support_sptr;
> +};
> +
> +/**
> + * struct intel_ipc_dev_ops - IPC device ops structure.
> + *
> + * Call backs for IPC device specific operations.
> + *
> + * @to_err_code         : Status to error code conversion function.
> + * @busy_check          : Check for IPC busy status.
> + * @enable_msi          : Enable MSI for IPC commands.
> + * @pre_simple_cmd_fn   : Custom pre-processing function for
> + *                        ipc_dev_simple_cmd()
> + * @pre_cmd_fn          : Custom pre-processing function for
> + *                        ipc_dev_cmd()
> + * @pre_raw_cmd_fn      : Custom pre-processing function for
> + *                        ipc_dev_raw_cmd()
> + *
> + */
> +struct intel_ipc_dev_ops {
> +	int (*to_err_code)(int status);
> +	int (*busy_check)(int status);
> +	u32 (*enable_msi)(u32 cmd);
> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> +			u32 *out, u32 outlen);
> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq); };
> +
> +/**
> + * struct intel_ipc_dev - Intel IPC device structure.
> + *
> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
> + *
> + * @dev                 : IPC device object.
> + * @cmd                 : Current IPC device command.
> + * @cmd_complete        : Command completion object.
> + * @lock                : Lock to protect IPC device structure.
> + * @ops                 : IPC device ops pointer.
> + * @cfg                 : IPC device cfg pointer.
> + *
> + */
> +struct intel_ipc_dev {
> +	struct device dev;
> +	int cmd;
> +	struct completion cmd_complete;
> +	struct mutex lock;
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
> +
> +/* API to create new IPC device */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops);
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen);
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
> ipc_dev_raw_cmd(struct
> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
> struct
> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name);
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	put_device(&ipc_dev->dev);
> +}
> +#else
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
> +		struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
> +		u32 *cmd_list, u32 cmdlen)
> +{
> +	return -EINVAL;
> +}
> +
> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
> *cmd_list,
> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
> +		u32 dptr, u32 sptr);
> +{
> +	return -EINVAL;
> +}
> +
> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
> +*dev_name) {
> +	return NULL;
> +}
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device
> *dev,
> +					const char *dev_name);
> +{
> +	return NULL;
> +}
> +
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	return NULL;
> +}
> +#endif /* CONFIG_INTEL_IPC_DEV */
> +#endif /* INTEL_IPC_DEV_H */
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-09  4:53     ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  4:53 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c redundantly
> implements the same IPC features and has lot of code duplication between
> them. This driver addresses this issue by grouping the common IPC
> functionalities under the same driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  drivers/platform/x86/Kconfig                    |   8 +
>  drivers/platform/x86/Makefile                   |   1 +
>  drivers/platform/x86/intel_ipc_dev.c            | 576
> ++++++++++++++++++++++++
>  include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
>  4 files changed, 791 insertions(+)
>  create mode 100644 drivers/platform/x86/intel_ipc_dev.c
>  create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Fixed NULL pointer exception in intel_ipc_dev_get().
>  * Fixed error in check for duplicate intel_ipc_dev.
>  * Added custom interrupt handler support.
>  * Used char array for error string conversion.
>  * Added put dev support.
>  * Added devm_* variant of intel_ipc_dev_get().
> 
> Changes since v2:
>  * Added ipc_dev_cmd API support.
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index da2d9ba..724ee696 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
>  	  with the OS-image for the device. This option supplies the missing
>  	  information. Enable this for x86 tablets with Silead touchscreens.
> 
> +config INTEL_IPC_DEV
> +	bool "Intel IPC Device Driver"
> +	depends on X86_64
> +	---help---
> +	  This driver implements core features of Intel IPC device. Devices
> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
> +	  driver to implement IPC protocol of their respective device.
> +
>  endif # X86_PLATFORM_DEVICES
> 
>  config PMC_ATOM
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 2b315d0..99a1af1 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
> pmc_atom.o
>  obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
>  obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
>  obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
> diff --git a/drivers/platform/x86/intel_ipc_dev.c
> b/drivers/platform/x86/intel_ipc_dev.c
> new file mode 100644
> index 0000000..f55ddec
> --- /dev/null
> +++ b/drivers/platform/x86/intel_ipc_dev.c
> @@ -0,0 +1,576 @@
> +/*
> + * intel_ipc_dev.c: Intel IPC device class driver
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +#include <linux/regmap.h>
> +
> +/* mutex to sync different ipc devices in same channel */ static struct
> +mutex channel_lock[IPC_CHANNEL_MAX];
> +
> +static char *ipc_err_sources[] = {
> +	[IPC_DEV_ERR_NONE] =
> +		"No error",
> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
> +		"Command not-supported/Invalid",
> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
> +		"Command not-serviced/Invalid param",
> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
> +		"Unable-to-service/Cmd-timeout",
> +	[IPC_DEV_ERR_CMD_INVALID] =
> +		"Command-invalid/Cmd-locked",
> +	[IPC_DEV_ERR_CMD_FAILED] =
> +		"Command-failed/Invalid-VR-id",
> +	[IPC_DEV_ERR_EMSECURITY] =
> +		"Invalid Battery/VR-Error",
> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
> +		"Unsigned kernel",
> +};
> +
> +static void ipc_channel_lock_init(void) {
> +	int i;
> +
> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
> +		mutex_init(&channel_lock[i]);
> +}
> +
> +static struct class intel_ipc_class = {
> +	.name = "intel_ipc",
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* acquire channel lock */
> +	mutex_lock(&channel_lock[chan_type]);
> +
> +	/* acquire IPC device lock */
> +	mutex_lock(&ipc_dev->lock);
> +
> +	return 0;
> +}
> +
> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* release IPC device lock */
> +	mutex_unlock(&ipc_dev->lock);
> +
> +	/* release channel lock */
> +	mutex_unlock(&channel_lock[chan_type]);
> +
> +	return 0;
> +}
> +
> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
> +	int error)
> +{
> +	if (error < IPC_DEV_ERR_MAX)
> +		return ipc_err_sources[error];
> +
> +	return "Unknown Command";
> +}
> +
> +/* Helper function to send given command to IPC device */ static inline
> +void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
> +	ipc_dev->cmd = cmd;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
> +		reinit_completion(&ipc_dev->cmd_complete);
> +
> +	if (ipc_dev->ops->enable_msi)
> +		cmd = ipc_dev->ops->enable_msi(cmd);
> +
> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
> }
> +
> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
> +	int status;
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->busy_check)
> +		return ipc_dev->ops->busy_check(status);
> +
> +	return 0;
> +}
> +
> +/* Check the status of IPC command and return err code if failed */
> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
> +	int status;
> +	int ret = 0;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
> +				IPC_DEV_CMD_TIMEOUT))
> +			ret = -ETIMEDOUT;
> +	} else {
> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
> +			udelay(1);
> +		if (!loop_count)
> +			ret = -ETIMEDOUT;
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
> >cmd);
> +		return ret;
> +	}
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->to_err_code)
> +		ret = ipc_dev->ops->to_err_code(status);
> +
> +	if (ret) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> +				ipc_dev_err_string(ipc_dev, ret),
> +				status, ipc_dev->cmd);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * ipc_dev_simple_cmd() - Send simple IPC command
> + * @ipc_dev     : Reference to ipc device.
> + * @cmd_list    : IPC command list.
> + * @cmdlen      : Number of cmd/sub-cmds.
> + *
> + * Send a simple IPC command to ipc device.
> + * Use this when don't need to specify input/output data
> + * and source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen)
> +{
> +	int ret;
> +
> +	if (!cmd_list)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler */
> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
> +
> +/**
> + * ipc_dev_cmd() - Send IPC command with data.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in dwords.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + *
> + * Send an IPC command to device with input/output data.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	int ret;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_cmd_fn) {
> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
> +				out, outlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
> +
> +/**
> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in bytes.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + * @dptr        : IPC destination data address.
> + * @sptr        : IPC source data address.
> + *
> + * Send an IPC command to device with input/output data and
> + * source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */

Sorry for coming in so late but since we are refactoring the API anyways, isn't it better to reduce the signature? Nine parameters is an awful lot and prone to errors and difficult to debug. (We found it out the hard way when we started using this a few years ago.)

This can be consolidated into a few neat structs, e.g.,:
int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd, 
		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)

Same for the ipc_dev_cmd() APIs above as well.

> +
> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
> +	int ret;
> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
> +	u32 *inbuf;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
> +	if (!inbuf)
> +		return -ENOMEM;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
> inlen,
> +				out, outlen, dptr, sptr);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* If supported, write DPTR register.*/
> +	if (ipc_dev->cfg->support_dptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >dptr_reg,
> +				dptr);
> +
> +	/* If supported, write SPTR register. */
> +	if (ipc_dev->cfg->support_sptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >sptr_reg,
> +				sptr);
> +
> +	memcpy(inbuf, in, inlen);
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +	kfree(inbuf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
> +
> +/* sysfs option to send simple IPC commands from userspace */ static
> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t count) {
> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
> +	u32 cmd;
> +	int ret;
> +
> +	ret = sscanf(buf, "%d", &cmd);
> +	if (ret != 1) {
> +		dev_err(dev, "Error args\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
> +	if (ret) {
> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
> +		return ret;
> +	}
> +	return (ssize_t)count;
> +}
> +
> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
> +
> +static struct attribute *ipc_dev_attrs[] = {
> +	&dev_attr_send_cmd.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group ipc_dev_group = {
> +	.attrs = ipc_dev_attrs,
> +};
> +
> +static const struct attribute_group *ipc_dev_groups[] = {
> +	&ipc_dev_group,
> +	NULL,
> +};
> +
> +/* IPC device IRQ handler */
> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
> +
> +	if (ipc_dev->ops->pre_irq_handler_fn)
> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
> +
> +	complete(&ipc_dev->cmd_complete);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
> +
> +	if (!ipc_dev)
> +		return;
> +
> +	device_del(&ipc_dev->dev);
> +
> +	kfree(ipc_dev);
> +}
> +
> +static int match_name(struct device *dev, const void *data) {
> +        if (!dev_name(dev))
> +                return 0;
> +
> +        return !strcmp(dev_name(dev), (char *)data); }
> +
> +/**
> + * intel_ipc_dev_get() - Get Intel IPC device from name.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
> +        struct device *dev;
> +
> +	if (!dev_name)
> +		return ERR_PTR(-EINVAL);
> +
> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
> match_name);
> +
> +	return dev ? dev_get_drvdata(dev) : NULL; }
> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
> +
> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
> +
> +/**
> + * devm_intel_ipc_dev_get() - Resource managed version of
> intel_ipc_dev_get().
> + * @dev         : Device pointer.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
> GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = intel_ipc_dev_get(dev_name);
> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
> +		*ptr = ipc_dev;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return ipc_dev;
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
> +
> +/**
> + * devm_intel_ipc_dev_create() - Create IPC device
> + * @dev		: IPC parent device.
> + * @devname	: Name of the IPC device.
> + * @cfg		: IPC device configuration.
> + * @ops		: IPC device ops.
> + *
> + * Resource managed API to create IPC device with
> + * given configuration.
> + *
> + * Return	: IPC device pointer or ERR_PTR(error code).
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname,
> +		struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +	int ret;
> +
> +	if (!dev && !devname && !cfg)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (intel_ipc_dev_get(devname)) {
> +		dev_err(dev, "IPC device %s already exist\n", devname);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
> +			GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
> +	if (!ipc_dev) {
> +		ret = -ENOMEM;
> +		goto err_dev_create;
> +	}
> +
> +	ipc_dev->dev.class = &intel_ipc_class;
> +	ipc_dev->dev.parent = dev;
> +	ipc_dev->dev.groups = ipc_dev_groups;
> +	ipc_dev->cfg = cfg;
> +	ipc_dev->ops = ops;
> +
> +	mutex_init(&ipc_dev->lock);
> +	init_completion(&ipc_dev->cmd_complete);
> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
> +	dev_set_name(&ipc_dev->dev, devname);
> +	device_initialize(&ipc_dev->dev);
> +
> +	ret = device_add(&ipc_dev->dev);
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
> +				__func__);
> +		ret = -ENODEV;
> +		goto err_dev_add;
> +	}
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (devm_request_irq(&ipc_dev->dev,
> +				ipc_dev->cfg->irq,
> +				ipc_dev_irq_handler,
> +				ipc_dev->cfg->irqflags,
> +				dev_name(&ipc_dev->dev),
> +				ipc_dev)) {
> +			dev_err(&ipc_dev->dev,
> +					"Failed to request irq\n");
> +			goto err_irq_request;
> +		}
> +	}
> +
> +	*ptr = ipc_dev;
> +
> +	devres_add(dev, ptr);
> +
> +	return ipc_dev;
> +
> +err_irq_request:
> +	device_del(&ipc_dev->dev);
> +err_dev_add:
> +	kfree(ipc_dev);
> +err_dev_create:
> +	devres_free(ptr);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
> +
> +static int __init intel_ipc_init(void)
> +{
> +	ipc_channel_lock_init();
> +	return class_register(&intel_ipc_class); }
> +
> +static void __exit intel_ipc_exit(void) {
> +	class_unregister(&intel_ipc_class);
> +}
> +subsys_initcall(intel_ipc_init);
> +module_exit(intel_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Kuppuswamy
> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
> +MODULE_DESCRIPTION("Intel IPC device class driver");
> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
> b/include/linux/platform_data/x86/intel_ipc_dev.h
> new file mode 100644
> index 0000000..eaeedaf
> --- /dev/null
> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
> @@ -0,0 +1,206 @@
> +/*
> + * Intel IPC class device header file.
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#ifndef INTEL_IPC_DEV_H
> +#define INTEL_IPC_DEV_H
> +
> +#include <linux/module.h>
> +#include <linux/device.h>
> +
> +/* IPC channel type */
> +#define IPC_CHANNEL_IA_PMC                      0
> +#define IPC_CHANNEL_IA_PUNIT                    1
> +#define IPC_CHANNEL_PMC_PUNIT                   2
> +#define IPC_CHANNEL_IA_SCU                      3
> +#define IPC_CHANNEL_MAX                         4
> +
> +/* IPC return code */
> +#define IPC_DEV_ERR_NONE			0
> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
> +#define IPC_DEV_ERR_CMD_INVALID			4
> +#define IPC_DEV_ERR_CMD_FAILED			5
> +#define IPC_DEV_ERR_EMSECURITY			6
> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
> +#define IPC_DEV_ERR_MAX				8
> +
> +/* IPC mode */
> +#define IPC_DEV_MODE_IRQ			0
> +#define IPC_DEV_MODE_POLLING			1
> +
> +/* IPC dev constants */
> +#define IPC_DEV_CMD_LOOP_CNT			3000000
> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
> +#define IPC_DEV_DATA_BUFFER_SIZE		16
> +
> +struct intel_ipc_dev;
> +struct intel_ipc_raw_cmd;
> +
> +/**
> + * struct intel_ipc_dev_cfg - IPC device config structure.
> + *
> + * IPC device drivers uses the following config options to
> + * register new IPC device.
> + *
> + * @cmd_regs            : IPC device command base regmap.
> + * @data_regs           : IPC device data base regmap.
> + * @wrbuf_reg           : IPC device data write register address.
> + * @rbuf_reg            : IPC device data read register address.
> + * @sptr_reg            : IPC device source data pointer register address.
> + * @dptr_reg            : IPC device destination data pointer register
> + *                        address.
> + * @status_reg          : IPC command status register address.
> + * @cmd_reg             : IPC command register address.
> + * @mode                : IRQ/POLLING mode.
> + * @irq                 : IPC device IRQ number.
> + * @irqflags            : IPC device IRQ flags.
> + * @chan_type           : IPC device channel type(PMC/PUNIT).
> + * @msi                 : Enable/Disable MSI for IPC commands.
> + * @support_dptr        : Support DPTR update.
> + * @support_sptr        : Support SPTR update.
> + *
> + */
> +struct intel_ipc_dev_cfg {
> +	struct regmap *cmd_regs;
> +	struct regmap *data_regs;
> +	unsigned int wrbuf_reg;
> +	unsigned int rbuf_reg;
> +	unsigned int sptr_reg;
> +	unsigned int dptr_reg;
> +	unsigned int status_reg;
> +	unsigned int cmd_reg;
> +	int mode;
> +	int irq;
> +	int irqflags;
> +	int chan_type;
> +	bool use_msi;
> +	bool support_dptr;
> +	bool support_sptr;
> +};
> +
> +/**
> + * struct intel_ipc_dev_ops - IPC device ops structure.
> + *
> + * Call backs for IPC device specific operations.
> + *
> + * @to_err_code         : Status to error code conversion function.
> + * @busy_check          : Check for IPC busy status.
> + * @enable_msi          : Enable MSI for IPC commands.
> + * @pre_simple_cmd_fn   : Custom pre-processing function for
> + *                        ipc_dev_simple_cmd()
> + * @pre_cmd_fn          : Custom pre-processing function for
> + *                        ipc_dev_cmd()
> + * @pre_raw_cmd_fn      : Custom pre-processing function for
> + *                        ipc_dev_raw_cmd()
> + *
> + */
> +struct intel_ipc_dev_ops {
> +	int (*to_err_code)(int status);
> +	int (*busy_check)(int status);
> +	u32 (*enable_msi)(u32 cmd);
> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> +			u32 *out, u32 outlen);
> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq); };
> +
> +/**
> + * struct intel_ipc_dev - Intel IPC device structure.
> + *
> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
> + *
> + * @dev                 : IPC device object.
> + * @cmd                 : Current IPC device command.
> + * @cmd_complete        : Command completion object.
> + * @lock                : Lock to protect IPC device structure.
> + * @ops                 : IPC device ops pointer.
> + * @cfg                 : IPC device cfg pointer.
> + *
> + */
> +struct intel_ipc_dev {
> +	struct device dev;
> +	int cmd;
> +	struct completion cmd_complete;
> +	struct mutex lock;
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
> +
> +/* API to create new IPC device */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops);
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen);
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
> ipc_dev_raw_cmd(struct
> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
> struct
> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name);
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	put_device(&ipc_dev->dev);
> +}
> +#else
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
> +		struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
> +		u32 *cmd_list, u32 cmdlen)
> +{
> +	return -EINVAL;
> +}
> +
> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
> *cmd_list,
> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
> +		u32 dptr, u32 sptr);
> +{
> +	return -EINVAL;
> +}
> +
> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
> +*dev_name) {
> +	return NULL;
> +}
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device
> *dev,
> +					const char *dev_name);
> +{
> +	return NULL;
> +}
> +
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	return NULL;
> +}
> +#endif /* CONFIG_INTEL_IPC_DEV */
> +#endif /* INTEL_IPC_DEV_H */
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-09  4:53     ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  4:53 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, x86-DgEjT+Ai2ygdnm+yROfE0A,
	wim-IQzOog9fTRqzQB+pC5nmwQ, mingo-H+wXaHxf7aLQT0dZR+AlfA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Zha, Qipeng,
	hpa-YMNOUZJC4hwAvxtiuMwx3w, dvhart-wEGCiKHe2LqWVfeAwA7xHQ,
	tglx-hfZtesqFncYOwBW4kG4KsQ, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	andy-wEGCiKHe2LqWVfeAwA7xHQ
  Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	platform-driver-x86-u79uwXL29TY76Z2rM5mHXA,
	sathyaosid-Re5JQEeQqe8AvxtiuMwx3w

> From: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
> [mailto:sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org; x86-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org;
> mingo-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org; alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org; Zha, Qipeng
> <qipeng.zha-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; hpa-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org; dvhart-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org;
> tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org; lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org; andy-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org; Chakravarty,
> Souvik K <souvik.k.chakravarty-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-watchdog-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
> kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; platform-driver-x86-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> sathyaosid-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> 
> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c redundantly
> implements the same IPC features and has lot of code duplication between
> them. This driver addresses this issue by grouping the common IPC
> functionalities under the same driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> ---
>  drivers/platform/x86/Kconfig                    |   8 +
>  drivers/platform/x86/Makefile                   |   1 +
>  drivers/platform/x86/intel_ipc_dev.c            | 576
> ++++++++++++++++++++++++
>  include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
>  4 files changed, 791 insertions(+)
>  create mode 100644 drivers/platform/x86/intel_ipc_dev.c
>  create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Fixed NULL pointer exception in intel_ipc_dev_get().
>  * Fixed error in check for duplicate intel_ipc_dev.
>  * Added custom interrupt handler support.
>  * Used char array for error string conversion.
>  * Added put dev support.
>  * Added devm_* variant of intel_ipc_dev_get().
> 
> Changes since v2:
>  * Added ipc_dev_cmd API support.
> 
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index da2d9ba..724ee696 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
>  	  with the OS-image for the device. This option supplies the missing
>  	  information. Enable this for x86 tablets with Silead touchscreens.
> 
> +config INTEL_IPC_DEV
> +	bool "Intel IPC Device Driver"
> +	depends on X86_64
> +	---help---
> +	  This driver implements core features of Intel IPC device. Devices
> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
> +	  driver to implement IPC protocol of their respective device.
> +
>  endif # X86_PLATFORM_DEVICES
> 
>  config PMC_ATOM
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 2b315d0..99a1af1 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
> pmc_atom.o
>  obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
>  obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
>  obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
> diff --git a/drivers/platform/x86/intel_ipc_dev.c
> b/drivers/platform/x86/intel_ipc_dev.c
> new file mode 100644
> index 0000000..f55ddec
> --- /dev/null
> +++ b/drivers/platform/x86/intel_ipc_dev.c
> @@ -0,0 +1,576 @@
> +/*
> + * intel_ipc_dev.c: Intel IPC device class driver
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +#include <linux/regmap.h>
> +
> +/* mutex to sync different ipc devices in same channel */ static struct
> +mutex channel_lock[IPC_CHANNEL_MAX];
> +
> +static char *ipc_err_sources[] = {
> +	[IPC_DEV_ERR_NONE] =
> +		"No error",
> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
> +		"Command not-supported/Invalid",
> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
> +		"Command not-serviced/Invalid param",
> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
> +		"Unable-to-service/Cmd-timeout",
> +	[IPC_DEV_ERR_CMD_INVALID] =
> +		"Command-invalid/Cmd-locked",
> +	[IPC_DEV_ERR_CMD_FAILED] =
> +		"Command-failed/Invalid-VR-id",
> +	[IPC_DEV_ERR_EMSECURITY] =
> +		"Invalid Battery/VR-Error",
> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
> +		"Unsigned kernel",
> +};
> +
> +static void ipc_channel_lock_init(void) {
> +	int i;
> +
> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
> +		mutex_init(&channel_lock[i]);
> +}
> +
> +static struct class intel_ipc_class = {
> +	.name = "intel_ipc",
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* acquire channel lock */
> +	mutex_lock(&channel_lock[chan_type]);
> +
> +	/* acquire IPC device lock */
> +	mutex_lock(&ipc_dev->lock);
> +
> +	return 0;
> +}
> +
> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
> +	int chan_type;
> +
> +	if (!ipc_dev || !ipc_dev->cfg)
> +		return -ENODEV;
> +
> +	chan_type = ipc_dev->cfg->chan_type;
> +	if (chan_type > IPC_CHANNEL_MAX)
> +		return -EINVAL;
> +
> +	/* release IPC device lock */
> +	mutex_unlock(&ipc_dev->lock);
> +
> +	/* release channel lock */
> +	mutex_unlock(&channel_lock[chan_type]);
> +
> +	return 0;
> +}
> +
> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
> +	int error)
> +{
> +	if (error < IPC_DEV_ERR_MAX)
> +		return ipc_err_sources[error];
> +
> +	return "Unknown Command";
> +}
> +
> +/* Helper function to send given command to IPC device */ static inline
> +void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
> +	ipc_dev->cmd = cmd;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
> +		reinit_completion(&ipc_dev->cmd_complete);
> +
> +	if (ipc_dev->ops->enable_msi)
> +		cmd = ipc_dev->ops->enable_msi(cmd);
> +
> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
> }
> +
> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
> +	int status;
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->busy_check)
> +		return ipc_dev->ops->busy_check(status);
> +
> +	return 0;
> +}
> +
> +/* Check the status of IPC command and return err code if failed */
> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
> +	int status;
> +	int ret = 0;
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
> +				IPC_DEV_CMD_TIMEOUT))
> +			ret = -ETIMEDOUT;
> +	} else {
> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
> +			udelay(1);
> +		if (!loop_count)
> +			ret = -ETIMEDOUT;
> +	}
> +
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
> >cmd);
> +		return ret;
> +	}
> +
> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> +&status);
> +
> +	if (ipc_dev->ops->to_err_code)
> +		ret = ipc_dev->ops->to_err_code(status);
> +
> +	if (ret) {
> +		dev_err(&ipc_dev->dev,
> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> +				ipc_dev_err_string(ipc_dev, ret),
> +				status, ipc_dev->cmd);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * ipc_dev_simple_cmd() - Send simple IPC command
> + * @ipc_dev     : Reference to ipc device.
> + * @cmd_list    : IPC command list.
> + * @cmdlen      : Number of cmd/sub-cmds.
> + *
> + * Send a simple IPC command to ipc device.
> + * Use this when don't need to specify input/output data
> + * and source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen)
> +{
> +	int ret;
> +
> +	if (!cmd_list)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler */
> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
> +
> +/**
> + * ipc_dev_cmd() - Send IPC command with data.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in dwords.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + *
> + * Send an IPC command to device with input/output data.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	int ret;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_cmd_fn) {
> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
> +				out, outlen);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
> +
> +/**
> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
> + * @ipc_dev     : Reference to ipc_dev.
> + * @cmd_list    : Array of commands/sub-commands.
> + * @cmdlen      : Number of commands.
> + * @in          : Input data of this IPC command.
> + * @inlen       : Input data length in bytes.
> + * @out         : Output data of this IPC command.
> + * @outlen      : Length of output data in dwords.
> + * @dptr        : IPC destination data address.
> + * @sptr        : IPC source data address.
> + *
> + * Send an IPC command to device with input/output data and
> + * source/dest pointers.
> + *
> + * Return:	an IPC error code or 0 on success.
> + */

Sorry for coming in so late but since we are refactoring the API anyways, isn't it better to reduce the signature? Nine parameters is an awful lot and prone to errors and difficult to debug. (We found it out the hard way when we started using this a few years ago.)

This can be consolidated into a few neat structs, e.g.,:
int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd, 
		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)

Same for the ipc_dev_cmd() APIs above as well.

> +
> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
> +	int ret;
> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
> +	u32 *inbuf;
> +
> +	if (!cmd_list || !in)
> +		return -EINVAL;
> +
> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
> +	if (!inbuf)
> +		return -ENOMEM;
> +
> +	ret = ipc_dev_lock(ipc_dev);
> +	if (ret)
> +		return ret;
> +
> +	/* Call custom pre-processing handler. */
> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
> inlen,
> +				out, outlen, dptr, sptr);
> +		if (ret)
> +			goto unlock_device;
> +	}
> +
> +	/* If supported, write DPTR register.*/
> +	if (ipc_dev->cfg->support_dptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >dptr_reg,
> +				dptr);
> +
> +	/* If supported, write SPTR register. */
> +	if (ipc_dev->cfg->support_sptr)
> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >sptr_reg,
> +				sptr);
> +
> +	memcpy(inbuf, in, inlen);
> +
> +	/* Write inlen dwords of data to wrbuf_reg. */
> +	if (inlen > 0)
> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
> +
> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> +
> +	ret = ipc_dev_check_status(ipc_dev);
> +
> +	/* Read outlen dwords of data from rbug_reg. */
> +	if (!ret && outlen > 0)
> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> +unlock_device:
> +	ipc_dev_unlock(ipc_dev);
> +	kfree(inbuf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
> +
> +/* sysfs option to send simple IPC commands from userspace */ static
> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t count) {
> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
> +	u32 cmd;
> +	int ret;
> +
> +	ret = sscanf(buf, "%d", &cmd);
> +	if (ret != 1) {
> +		dev_err(dev, "Error args\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
> +	if (ret) {
> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
> +		return ret;
> +	}
> +	return (ssize_t)count;
> +}
> +
> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
> +
> +static struct attribute *ipc_dev_attrs[] = {
> +	&dev_attr_send_cmd.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group ipc_dev_group = {
> +	.attrs = ipc_dev_attrs,
> +};
> +
> +static const struct attribute_group *ipc_dev_groups[] = {
> +	&ipc_dev_group,
> +	NULL,
> +};
> +
> +/* IPC device IRQ handler */
> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
> +
> +	if (ipc_dev->ops->pre_irq_handler_fn)
> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
> +
> +	complete(&ipc_dev->cmd_complete);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
> +
> +	if (!ipc_dev)
> +		return;
> +
> +	device_del(&ipc_dev->dev);
> +
> +	kfree(ipc_dev);
> +}
> +
> +static int match_name(struct device *dev, const void *data) {
> +        if (!dev_name(dev))
> +                return 0;
> +
> +        return !strcmp(dev_name(dev), (char *)data); }
> +
> +/**
> + * intel_ipc_dev_get() - Get Intel IPC device from name.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
> +        struct device *dev;
> +
> +	if (!dev_name)
> +		return ERR_PTR(-EINVAL);
> +
> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
> match_name);
> +
> +	return dev ? dev_get_drvdata(dev) : NULL; }
> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
> +
> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
> +
> +/**
> + * devm_intel_ipc_dev_get() - Resource managed version of
> intel_ipc_dev_get().
> + * @dev         : Device pointer.
> + * @dev_name    : Name of the IPC device.
> + *
> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
> GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = intel_ipc_dev_get(dev_name);
> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
> +		*ptr = ipc_dev;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return ipc_dev;
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
> +
> +/**
> + * devm_intel_ipc_dev_create() - Create IPC device
> + * @dev		: IPC parent device.
> + * @devname	: Name of the IPC device.
> + * @cfg		: IPC device configuration.
> + * @ops		: IPC device ops.
> + *
> + * Resource managed API to create IPC device with
> + * given configuration.
> + *
> + * Return	: IPC device pointer or ERR_PTR(error code).
> + */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname,
> +		struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	struct intel_ipc_dev **ptr, *ipc_dev;
> +	int ret;
> +
> +	if (!dev && !devname && !cfg)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (intel_ipc_dev_get(devname)) {
> +		dev_err(dev, "IPC device %s already exist\n", devname);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
> +			GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
> +	if (!ipc_dev) {
> +		ret = -ENOMEM;
> +		goto err_dev_create;
> +	}
> +
> +	ipc_dev->dev.class = &intel_ipc_class;
> +	ipc_dev->dev.parent = dev;
> +	ipc_dev->dev.groups = ipc_dev_groups;
> +	ipc_dev->cfg = cfg;
> +	ipc_dev->ops = ops;
> +
> +	mutex_init(&ipc_dev->lock);
> +	init_completion(&ipc_dev->cmd_complete);
> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
> +	dev_set_name(&ipc_dev->dev, devname);
> +	device_initialize(&ipc_dev->dev);
> +
> +	ret = device_add(&ipc_dev->dev);
> +	if (ret < 0) {
> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
> +				__func__);
> +		ret = -ENODEV;
> +		goto err_dev_add;
> +	}
> +
> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> +		if (devm_request_irq(&ipc_dev->dev,
> +				ipc_dev->cfg->irq,
> +				ipc_dev_irq_handler,
> +				ipc_dev->cfg->irqflags,
> +				dev_name(&ipc_dev->dev),
> +				ipc_dev)) {
> +			dev_err(&ipc_dev->dev,
> +					"Failed to request irq\n");
> +			goto err_irq_request;
> +		}
> +	}
> +
> +	*ptr = ipc_dev;
> +
> +	devres_add(dev, ptr);
> +
> +	return ipc_dev;
> +
> +err_irq_request:
> +	device_del(&ipc_dev->dev);
> +err_dev_add:
> +	kfree(ipc_dev);
> +err_dev_create:
> +	devres_free(ptr);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
> +
> +static int __init intel_ipc_init(void)
> +{
> +	ipc_channel_lock_init();
> +	return class_register(&intel_ipc_class); }
> +
> +static void __exit intel_ipc_exit(void) {
> +	class_unregister(&intel_ipc_class);
> +}
> +subsys_initcall(intel_ipc_init);
> +module_exit(intel_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Kuppuswamy
> +Sathyanarayanan<sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>");
> +MODULE_DESCRIPTION("Intel IPC device class driver");
> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
> b/include/linux/platform_data/x86/intel_ipc_dev.h
> new file mode 100644
> index 0000000..eaeedaf
> --- /dev/null
> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
> @@ -0,0 +1,206 @@
> +/*
> + * Intel IPC class device header file.
> + *
> + * (C) Copyright 2017 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; version 2
> + * of the License.
> + *
> + */
> +
> +#ifndef INTEL_IPC_DEV_H
> +#define INTEL_IPC_DEV_H
> +
> +#include <linux/module.h>
> +#include <linux/device.h>
> +
> +/* IPC channel type */
> +#define IPC_CHANNEL_IA_PMC                      0
> +#define IPC_CHANNEL_IA_PUNIT                    1
> +#define IPC_CHANNEL_PMC_PUNIT                   2
> +#define IPC_CHANNEL_IA_SCU                      3
> +#define IPC_CHANNEL_MAX                         4
> +
> +/* IPC return code */
> +#define IPC_DEV_ERR_NONE			0
> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
> +#define IPC_DEV_ERR_CMD_INVALID			4
> +#define IPC_DEV_ERR_CMD_FAILED			5
> +#define IPC_DEV_ERR_EMSECURITY			6
> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
> +#define IPC_DEV_ERR_MAX				8
> +
> +/* IPC mode */
> +#define IPC_DEV_MODE_IRQ			0
> +#define IPC_DEV_MODE_POLLING			1
> +
> +/* IPC dev constants */
> +#define IPC_DEV_CMD_LOOP_CNT			3000000
> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
> +#define IPC_DEV_DATA_BUFFER_SIZE		16
> +
> +struct intel_ipc_dev;
> +struct intel_ipc_raw_cmd;
> +
> +/**
> + * struct intel_ipc_dev_cfg - IPC device config structure.
> + *
> + * IPC device drivers uses the following config options to
> + * register new IPC device.
> + *
> + * @cmd_regs            : IPC device command base regmap.
> + * @data_regs           : IPC device data base regmap.
> + * @wrbuf_reg           : IPC device data write register address.
> + * @rbuf_reg            : IPC device data read register address.
> + * @sptr_reg            : IPC device source data pointer register address.
> + * @dptr_reg            : IPC device destination data pointer register
> + *                        address.
> + * @status_reg          : IPC command status register address.
> + * @cmd_reg             : IPC command register address.
> + * @mode                : IRQ/POLLING mode.
> + * @irq                 : IPC device IRQ number.
> + * @irqflags            : IPC device IRQ flags.
> + * @chan_type           : IPC device channel type(PMC/PUNIT).
> + * @msi                 : Enable/Disable MSI for IPC commands.
> + * @support_dptr        : Support DPTR update.
> + * @support_sptr        : Support SPTR update.
> + *
> + */
> +struct intel_ipc_dev_cfg {
> +	struct regmap *cmd_regs;
> +	struct regmap *data_regs;
> +	unsigned int wrbuf_reg;
> +	unsigned int rbuf_reg;
> +	unsigned int sptr_reg;
> +	unsigned int dptr_reg;
> +	unsigned int status_reg;
> +	unsigned int cmd_reg;
> +	int mode;
> +	int irq;
> +	int irqflags;
> +	int chan_type;
> +	bool use_msi;
> +	bool support_dptr;
> +	bool support_sptr;
> +};
> +
> +/**
> + * struct intel_ipc_dev_ops - IPC device ops structure.
> + *
> + * Call backs for IPC device specific operations.
> + *
> + * @to_err_code         : Status to error code conversion function.
> + * @busy_check          : Check for IPC busy status.
> + * @enable_msi          : Enable MSI for IPC commands.
> + * @pre_simple_cmd_fn   : Custom pre-processing function for
> + *                        ipc_dev_simple_cmd()
> + * @pre_cmd_fn          : Custom pre-processing function for
> + *                        ipc_dev_cmd()
> + * @pre_raw_cmd_fn      : Custom pre-processing function for
> + *                        ipc_dev_raw_cmd()
> + *
> + */
> +struct intel_ipc_dev_ops {
> +	int (*to_err_code)(int status);
> +	int (*busy_check)(int status);
> +	u32 (*enable_msi)(u32 cmd);
> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> +			u32 *out, u32 outlen);
> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq); };
> +
> +/**
> + * struct intel_ipc_dev - Intel IPC device structure.
> + *
> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
> + *
> + * @dev                 : IPC device object.
> + * @cmd                 : Current IPC device command.
> + * @cmd_complete        : Command completion object.
> + * @lock                : Lock to protect IPC device structure.
> + * @ops                 : IPC device ops pointer.
> + * @cfg                 : IPC device cfg pointer.
> + *
> + */
> +struct intel_ipc_dev {
> +	struct device dev;
> +	int cmd;
> +	struct completion cmd_complete;
> +	struct mutex lock;
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +};
> +
> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
> +
> +/* API to create new IPC device */
> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops);
> +
> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen);
> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
> ipc_dev_raw_cmd(struct
> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
> struct
> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> +					const char *dev_name);
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	put_device(&ipc_dev->dev);
> +}
> +#else
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
> +		struct device *dev,
> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> +		struct intel_ipc_dev_ops *ops)
> +{
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
> +		u32 *cmd_list, u32 cmdlen)
> +{
> +	return -EINVAL;
> +}
> +
> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
> +	return -EINVAL;
> +}
> +
> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
> *cmd_list,
> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
> +		u32 dptr, u32 sptr);
> +{
> +	return -EINVAL;
> +}
> +
> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
> +*dev_name) {
> +	return NULL;
> +}
> +
> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device
> *dev,
> +					const char *dev_name);
> +{
> +	return NULL;
> +}
> +
> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> +	return NULL;
> +}
> +#endif /* CONFIG_INTEL_IPC_DEV */
> +#endif /* INTEL_IPC_DEV_H */
> --
> 2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
@ 2017-10-09  5:07     ` Chakravarty, Souvik K
  -1 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  5:07 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
> device calls
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Removed redundant IPC helper functions and refactored the driver to use
> APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC user
> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
>  drivers/platform/x86/Kconfig                  |   1 +
>  drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
>  drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
>  4 files changed, 223 insertions(+), 303 deletions(-)
> 
> Changes since v4:
>  * None
> 
> Changes since v2:
>  * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
>  * Added intel_ipc_dev_put() support.
> 
> Changes since v1:
>  * Removed custom APIs.
>  * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
>    IPC driver.
> 
> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
> b/arch/x86/include/asm/intel_punit_ipc.h
> index 201eb9d..cf1630c 100644
> --- a/arch/x86/include/asm/intel_punit_ipc.h
> +++ b/arch/x86/include/asm/intel_punit_ipc.h
> @@ -1,10 +1,8 @@
>  #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
>  #define  _ASM_X86_INTEL_PUNIT_IPC_H_
> 
> -/*
> - * Three types of 8bit P-Unit IPC commands are supported,
> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
> - */
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +
>  typedef enum {
>  	BIOS_IPC = 0,
>  	GTDRIVER_IPC,
> @@ -12,61 +10,60 @@ typedef enum {
>  	RESERVED_IPC,
>  } IPC_TYPE;
> 
> -#define IPC_TYPE_OFFSET			6
> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
> IPC_TYPE_OFFSET)
> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
> +#define PUNIT_PARAM_LEN				3
> 
>  /* BIOS => Pcode commands */
> -#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_BIOS_VR_INTERFACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
> -#define IPC_PUNIT_BIOS_READ_PCS
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
> -#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE
> | 0x03)
> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE
> | 0x07)
> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
> -#define IPC_PUNIT_BIOS_RESERVED
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
> 
>  /* GT Driver => Pcode commands */
> -#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_GTD_CONFIG
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
> -#define IPC_PUNIT_GTD_GET_WM_VAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
> +#define IPC_PUNIT_GTD_ZERO			(0x00)
> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
> 
>  /* ISP Driver => Pcode commands */
> -#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_ISPD_CONFIG
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
> 
>  /* Error codes */
>  #define IPC_PUNIT_ERR_SUCCESS			0
> @@ -77,25 +74,11 @@ typedef enum {
>  #define IPC_PUNIT_ERR_INVALID_VR_ID		5
>  #define IPC_PUNIT_ERR_VR_ERR			6
> 
> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> -
> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2); -int
> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> *out);
> -
> -#else
> -
> -static inline int intel_punit_ipc_simple_command(int cmd,
> -						  int para1, int para2)
> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32
> +param3)
>  {
> -	return -ENODEV;
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +	cmd[2] = param3;
>  }
> 
> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
> -					  u32 *in, u32 *out)
> -{
> -	return -ENODEV;
> -}
> -
> -#endif /* CONFIG_INTEL_PUNIT_IPC */
> -
>  #endif
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 724ee696..9442c23 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
> 
>  config INTEL_PUNIT_IPC
>  	tristate "Intel P-Unit IPC Driver"
> +	select REGMAP_MMIO
>  	---help---
>  	  This driver provides support for Intel P-Unit Mailbox IPC
> mechanism,
>  	  which is used to bridge the communications between kernel and P-
> Unit.
> diff --git a/drivers/platform/x86/intel_punit_ipc.c
> b/drivers/platform/x86/intel_punit_ipc.c
> index b5b8901..f310a05 100644
> --- a/drivers/platform/x86/intel_punit_ipc.c
> +++ b/drivers/platform/x86/intel_punit_ipc.c
> @@ -18,18 +18,18 @@
>  #include <linux/device.h>
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +#include <linux/regmap.h>
>  #include <asm/intel_punit_ipc.h>
> 
> -/* IPC Mailbox registers */
> -#define OFFSET_DATA_LOW		0x0
> -#define OFFSET_DATA_HIGH	0x4
>  /* bit field of interface register */
>  #define	CMD_RUN			BIT(31)
> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
>  #define	CMD_PARA1_SHIFT		8
>  #define	CMD_PARA2_SHIFT		16
> 
> -#define CMD_TIMEOUT_SECONDS	1
> +/* IPC PUNIT commands */
> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
> 0)
> 
>  enum {
>  	BASE_DATA = 0,
> @@ -39,187 +39,42 @@ enum {
> 
>  typedef struct {
>  	struct device *dev;
> -	struct mutex lock;
> -	int irq;
> -	struct completion cmd_complete;
>  	/* base of interface and data registers */
>  	void __iomem *base[RESERVED_IPC][BASE_MAX];
> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
>  	IPC_TYPE type;
>  } IPC_DEV;
> 
>  static IPC_DEV *punit_ipcdev;
> 
> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_IFACE]);
> -}
> -
> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd) -
> {
> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
> -}
> -
> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> -}
> -
> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> -}
> -
> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32
> data) -{
> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> -}
> -
> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32
> data) -{
> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> -}
> +const char *ipc_dev_name[RESERVED_IPC] = {
> +	PUNIT_BIOS_IPC_DEV,
> +	PUNIT_GTD_IPC_DEV,
> +	PUNIT_ISP_IPC_DEV
> +};
> 
> -static const char *ipc_err_string(int error) -{
> -	if (error == IPC_PUNIT_ERR_SUCCESS)
> -		return "no error";
> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
> -		return "invalid command";
> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
> -		return "invalid parameter";
> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
> -		return "command timeout";
> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
> -		return "command locked";
> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
> -		return "invalid vr id";
> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
> -		return "vr error";
> -	else
> -		return "unknown error";
> -}
> +static struct regmap_config punit_regmap_config = {
> +        .reg_bits = 32,
> +        .reg_stride = 4,
> +        .val_bits = 32,
> +};
> 
> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>  {
> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> -	int errcode;
> -	int status;
> -
> -	if (ipcdev->irq) {
> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
> -						 CMD_TIMEOUT_SECONDS *
> HZ)) {
> -			dev_err(ipcdev->dev, "IPC timed out\n");
> -			return -ETIMEDOUT;
> -		}
> -	} else {
> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
> loops)
> -			udelay(1);
> -		if (!loops) {
> -			dev_err(ipcdev->dev, "IPC timed out\n");
> -			return -ETIMEDOUT;
> -		}
> -	}
> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
> +		return -EINVAL;
> 
> -	status = ipc_read_status(ipcdev, type);
> -	errcode = status & CMD_ERRCODE_MASK;
> -	if (errcode) {
> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
> -			ipc_err_string(errcode), status);
> -		return -EIO;
> -	}
> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
> +		cmd_list[2] << CMD_PARA1_SHIFT;
> 
>  	return 0;
>  }
> 
> -/**
> - * intel_punit_ipc_simple_command() - Simple IPC command
> - * @cmd:	IPC command code.
> - * @para1:	First 8bit parameter, set 0 if not used.
> - * @para2:	Second 8bit parameter, set 0 if not used.
> - *
> - * Send a IPC command to P-Unit when there is no data transaction
> - *
> - * Return:	IPC error code or 0 on success.
> - */
> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
> -	IPC_DEV *ipcdev = punit_ipcdev;
> -	IPC_TYPE type;
> -	u32 val;
> -	int ret;
> -
> -	mutex_lock(&ipcdev->lock);
> -
> -	reinit_completion(&ipcdev->cmd_complete);
> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> -
> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> CMD_PARA1_SHIFT;
> -	ipc_write_cmd(ipcdev, type, val);
> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> -
> -	mutex_unlock(&ipcdev->lock);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> -
> -/**
> - * intel_punit_ipc_command() - IPC command with data and pointers
> - * @cmd:	IPC command code.
> - * @para1:	First 8bit parameter, set 0 if not used.
> - * @para2:	Second 8bit parameter, set 0 if not used.
> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
> and ISPD.
> - * @out:	Output data.
> - *
> - * Send a IPC command to P-Unit with data transaction
> - *
> - * Return:	IPC error code or 0 on success.
> - */
> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> *out) -{
> -	IPC_DEV *ipcdev = punit_ipcdev;
> -	IPC_TYPE type;
> -	u32 val;
> -	int ret;
> -
> -	mutex_lock(&ipcdev->lock);
> -
> -	reinit_completion(&ipcdev->cmd_complete);
> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> -
> -	if (in) {
> -		ipc_write_data_low(ipcdev, type, *in);
> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> -			ipc_write_data_high(ipcdev, type, *++in);
> -	}
> -
> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> CMD_PARA1_SHIFT;
> -	ipc_write_cmd(ipcdev, type, val);
> -
> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> -	if (ret)
> -		goto out;
> -
> -	if (out) {
> -		*out = ipc_read_data_low(ipcdev, type);
> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> -			*++out = ipc_read_data_high(ipcdev, type);
> -	}
> -
> -out:
> -	mutex_unlock(&ipcdev->lock);
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> -
> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */ int
> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
> +		u32 outlen, u32 dptr, u32 sptr)
>  {
> -	IPC_DEV *ipcdev = dev_id;
> -
> -	complete(&ipcdev->cmd_complete);
> -	return IRQ_HANDLED;
> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
>  }
> 
>  static int intel_punit_get_bars(struct platform_device *pdev) @@ -282,9
> +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
>  	return 0;
>  }
> 
> +static int punit_ipc_err_code(int status) {
> +	return (status & CMD_ERRCODE_MASK);
> +}
> +
> +static int punit_ipc_busy_check(int status) {
> +	return status | CMD_RUN;
> +}
> +
> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
> +		const char *devname,
> +		int irq,
> +		void __iomem *base,
> +		void __iomem *data)
> +{
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +	struct regmap *cmd_regs, *data_regs;
> +
> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
> +        if (!cfg)
> +                return ERR_PTR(-ENOMEM);
> +
> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return ERR_PTR(-ENOMEM);
> +
> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> "%s_%s",
> +						  devname, "base");
> +
> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
> +			&punit_regmap_config);
> +	if (IS_ERR(cmd_regs)) {
> +                dev_err(dev, "cmd_regs regmap init failed\n");
> +                return ERR_CAST(cmd_regs);;
> +        }
> +
> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> "%s_%s",
> +						  devname, "data");
> +
> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
> +			&punit_regmap_config);
> +        if (IS_ERR(data_regs)) {
> +                dev_err(dev, "data_regs regmap init failed\n");
> +                return ERR_CAST(data_regs);;
> +        }
> +
> +	/* set IPC dev ops */
> +	ops->to_err_code = punit_ipc_err_code;
> +	ops->busy_check = punit_ipc_busy_check;
> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> +
> +	if (irq > 0)
> +	        cfg->mode = IPC_DEV_MODE_IRQ;
> +	else
> +	        cfg->mode = IPC_DEV_MODE_POLLING;
> +
> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
> +	cfg->irq = irq;
> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
> +	cfg->cmd_regs = cmd_regs;
> +	cfg->data_regs = data_regs;
> +
> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
> +
>  static int intel_punit_ipc_probe(struct platform_device *pdev)  {
> -	int irq, ret;
> +	int irq, ret, i;
> 
>  	punit_ipcdev = devm_kzalloc(&pdev->dev,
>  				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device
> *pdev)
>  	platform_set_drvdata(pdev, punit_ipcdev);
> 
>  	irq = platform_get_irq(pdev, 0);
> -	if (irq < 0) {
> -		punit_ipcdev->irq = 0;
> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
> -	} else {
> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
> -				       &punit_ipcdev);
> -		if (ret) {
> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
> irq);
> -			return ret;
> -		}
> -		punit_ipcdev->irq = irq;
> -	}
> 
>  	ret = intel_punit_get_bars(pdev);
>  	if (ret)
> -		goto out;
> +		return ret;
> +
> +	for (i = 0; i < RESERVED_IPC; i++) {
> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
> +				&pdev->dev,
> +				ipc_dev_name[i],
> +				irq,
> +				punit_ipcdev->base[i][BASE_IFACE],
> +				punit_ipcdev->base[i][BASE_DATA]);
> +
> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
> +			dev_err(&pdev->dev, "%s create failed\n",
> +					ipc_dev_name[i]);
> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
> +		}
> +	}
> 
>  	punit_ipcdev->dev = &pdev->dev;
> -	mutex_init(&punit_ipcdev->lock);
> -	init_completion(&punit_ipcdev->cmd_complete);
> 
> -out:
>  	return ret;
> -}
> 
> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
> -	return 0;
>  }
> 
>  static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@ -332,7 +250,6
> @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
> 
>  static struct platform_driver intel_punit_ipc_driver = {
>  	.probe = intel_punit_ipc_probe,
> -	.remove = intel_punit_ipc_remove,
>  	.driver = {
>  		.name = "intel_punit_ipc",
>  		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
> a/drivers/platform/x86/intel_telemetry_pltdrv.c
> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> index e0424d5..bf8284a 100644
> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c

This is a logical separation and so should be a different patch.

> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
> 
>  static struct telemetry_plt_config *telm_conf;
> +static struct intel_ipc_dev *punit_bios_ipc_dev;

Simply punit_ipc_dev is good enough.

> 
>  /*
>   * The following counters are programmed by default during setup.
> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
>  	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
>  };
> 
> -
>  static struct telemetry_evtmap
> 
> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
> NTS] = {
>  	{"IA_CORE0_C6_RES",			0x0400},
> @@ -283,13 +283,12 @@ static inline int
> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline int
> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
>  	u32 write_buf;
> -	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
> -				      index, 0, &write_buf, NULL);
> -
> -	return ret;
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
>  }

1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like
telemetry_punit_send_cmd(). Same thoughout

> 
>  static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
> evtconfig, @@ -435,6 +434,7 @@ static int
> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
>  	int ret, index, idx;
>  	u32 *pss_evtmap;
>  	u32 telem_ctrl;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	num_pss_evts = evtconfig.num_evts;
>  	pss_period = evtconfig.period;
> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* PSS Config */
>  	/* Get telemetry EVENT CTL */
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> -				      0, 0, NULL, &telem_ctrl);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN, NULL,
> +			0, &telem_ctrl, 1, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Read Failed\n");
>  		return ret;
> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* Disable Telemetry */
>  	TELEM_DISABLE(telem_ctrl);
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				      0, 0, &telem_ctrl, NULL);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
>  		return ret;
> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	TELEM_ENABLE_PERIODIC(telem_ctrl);
>  	telem_ctrl |= pss_period;
> 
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				      0, 0, &telem_ctrl, NULL);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
>  		return ret;
> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
> *pdev)  {
>  	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
>  	u32 read_buf, events, event_regs;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>  	int ret;
> 
>  	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
> telemetry_setup(struct platform_device *pdev)
>  	telm_conf->ioss_config.max_period =
> TELEM_MAX_PERIOD(read_buf);
> 
>  	/* PUNIT Mailbox Setup */
> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
> 0, 0,
> -				      NULL, &read_buf);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			NULL, 0, &read_buf, 1, 0, 0);
>  	if (ret) {
>  		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
>  		return ret;
> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)  {
>  	u32 telem_ctrl = 0;
>  	int ret = 0;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	mutex_lock(&(telm_conf->telem_lock));
>  	if (ioss_period) {
> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		}
> 
>  		/* Get telemetry EVENT CTL */
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> -				0, 0, NULL, &telem_ctrl);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &telem_ctrl, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Read Failed\n");
>  			goto out;
> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
> 
>  		/* Disable Telemetry */
>  		TELEM_DISABLE(telem_ctrl);
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> +				0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			goto out;
> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		TELEM_ENABLE_PERIODIC(telem_ctrl);
>  		telem_ctrl |= pss_period;
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> +				0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Enable Write
> Failed\n");
>  			goto out;
> @@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum
> telemetry_unit telem_unit,  {
>  	u32 temp = 0;
>  	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	if (verbosity == NULL)
>  		return -EINVAL;
> @@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum
> telemetry_unit telem_unit,
>  	mutex_lock(&(telm_conf->telem_trace_lock));
>  	switch (telem_unit) {
>  	case TELEM_PSS:
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> -				0, 0, NULL, &temp);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Read Failed\n");
>  			goto out;
> @@ -1058,15 +1071,16 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
>  	u32 temp = 0;
>  	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	verbosity &= TELEM_TRC_VERBOSITY_MASK;
> 
>  	mutex_lock(&(telm_conf->telem_trace_lock));
>  	switch (telem_unit) {
>  	case TELEM_PSS:
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> -				0, 0, NULL, &temp);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Read Failed\n");
>  			goto out;
> @@ -1074,10 +1088,10 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> 
>  		TELEM_CLEAR_VERBOSITY_BITS(temp);
>  		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> -
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
> -				0, 0, &temp, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> +				0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
>  			goto out;
> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
> platform_device *pdev)
>  	if (!id)
>  		return -ENODEV;
> 
> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
> +		return PTR_ERR(punit_bios_ipc_dev);
> +
>  	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> 
>  	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device
> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>  	telemetry_clear_pltdata();
> +	intel_ipc_dev_put(punit_bios_ipc_dev);
>  	iounmap(telm_conf->pss_config.regmap);
>  	iounmap(telm_conf->ioss_config.regmap);
> 
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
@ 2017-10-09  5:07     ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  5:07 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
> device calls
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Removed redundant IPC helper functions and refactored the driver to use
> APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC user
> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
>  drivers/platform/x86/Kconfig                  |   1 +
>  drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
>  drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
>  4 files changed, 223 insertions(+), 303 deletions(-)
> 
> Changes since v4:
>  * None
> 
> Changes since v2:
>  * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
>  * Added intel_ipc_dev_put() support.
> 
> Changes since v1:
>  * Removed custom APIs.
>  * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
>    IPC driver.
> 
> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
> b/arch/x86/include/asm/intel_punit_ipc.h
> index 201eb9d..cf1630c 100644
> --- a/arch/x86/include/asm/intel_punit_ipc.h
> +++ b/arch/x86/include/asm/intel_punit_ipc.h
> @@ -1,10 +1,8 @@
>  #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
>  #define  _ASM_X86_INTEL_PUNIT_IPC_H_
> 
> -/*
> - * Three types of 8bit P-Unit IPC commands are supported,
> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
> - */
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +
>  typedef enum {
>  	BIOS_IPC = 0,
>  	GTDRIVER_IPC,
> @@ -12,61 +10,60 @@ typedef enum {
>  	RESERVED_IPC,
>  } IPC_TYPE;
> 
> -#define IPC_TYPE_OFFSET			6
> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
> IPC_TYPE_OFFSET)
> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
> IPC_TYPE_OFFSET)
> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
> +#define PUNIT_PARAM_LEN				3
> 
>  /* BIOS => Pcode commands */
> -#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_BIOS_VR_INTERFACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
> -#define IPC_PUNIT_BIOS_READ_PCS
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
> -#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE
> | 0x03)
> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE
> | 0x07)
> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
> -#define IPC_PUNIT_BIOS_RESERVED
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
> 
>  /* GT Driver => Pcode commands */
> -#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_GTD_CONFIG
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
> -#define IPC_PUNIT_GTD_GET_WM_VAL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
> +#define IPC_PUNIT_GTD_ZERO			(0x00)
> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
> 
>  /* ISP Driver => Pcode commands */
> -#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE
> | 0x00)
> -#define IPC_PUNIT_ISPD_CONFIG
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
> 
>  /* Error codes */
>  #define IPC_PUNIT_ERR_SUCCESS			0
> @@ -77,25 +74,11 @@ typedef enum {
>  #define IPC_PUNIT_ERR_INVALID_VR_ID		5
>  #define IPC_PUNIT_ERR_VR_ERR			6
> 
> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> -
> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2); -int
> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> *out);
> -
> -#else
> -
> -static inline int intel_punit_ipc_simple_command(int cmd,
> -						  int para1, int para2)
> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32
> +param3)
>  {
> -	return -ENODEV;
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +	cmd[2] = param3;
>  }
> 
> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
> -					  u32 *in, u32 *out)
> -{
> -	return -ENODEV;
> -}
> -
> -#endif /* CONFIG_INTEL_PUNIT_IPC */
> -
>  #endif
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 724ee696..9442c23 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
> 
>  config INTEL_PUNIT_IPC
>  	tristate "Intel P-Unit IPC Driver"
> +	select REGMAP_MMIO
>  	---help---
>  	  This driver provides support for Intel P-Unit Mailbox IPC
> mechanism,
>  	  which is used to bridge the communications between kernel and P-
> Unit.
> diff --git a/drivers/platform/x86/intel_punit_ipc.c
> b/drivers/platform/x86/intel_punit_ipc.c
> index b5b8901..f310a05 100644
> --- a/drivers/platform/x86/intel_punit_ipc.c
> +++ b/drivers/platform/x86/intel_punit_ipc.c
> @@ -18,18 +18,18 @@
>  #include <linux/device.h>
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +#include <linux/regmap.h>
>  #include <asm/intel_punit_ipc.h>
> 
> -/* IPC Mailbox registers */
> -#define OFFSET_DATA_LOW		0x0
> -#define OFFSET_DATA_HIGH	0x4
>  /* bit field of interface register */
>  #define	CMD_RUN			BIT(31)
> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
>  #define	CMD_PARA1_SHIFT		8
>  #define	CMD_PARA2_SHIFT		16
> 
> -#define CMD_TIMEOUT_SECONDS	1
> +/* IPC PUNIT commands */
> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
> 0)
> 
>  enum {
>  	BASE_DATA = 0,
> @@ -39,187 +39,42 @@ enum {
> 
>  typedef struct {
>  	struct device *dev;
> -	struct mutex lock;
> -	int irq;
> -	struct completion cmd_complete;
>  	/* base of interface and data registers */
>  	void __iomem *base[RESERVED_IPC][BASE_MAX];
> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
>  	IPC_TYPE type;
>  } IPC_DEV;
> 
>  static IPC_DEV *punit_ipcdev;
> 
> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_IFACE]);
> -}
> -
> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd) -
> {
> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
> -}
> -
> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> -}
> -
> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> -}
> -
> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32
> data) -{
> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> -}
> -
> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32
> data) -{
> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> -}
> +const char *ipc_dev_name[RESERVED_IPC] = {
> +	PUNIT_BIOS_IPC_DEV,
> +	PUNIT_GTD_IPC_DEV,
> +	PUNIT_ISP_IPC_DEV
> +};
> 
> -static const char *ipc_err_string(int error) -{
> -	if (error == IPC_PUNIT_ERR_SUCCESS)
> -		return "no error";
> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
> -		return "invalid command";
> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
> -		return "invalid parameter";
> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
> -		return "command timeout";
> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
> -		return "command locked";
> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
> -		return "invalid vr id";
> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
> -		return "vr error";
> -	else
> -		return "unknown error";
> -}
> +static struct regmap_config punit_regmap_config = {
> +        .reg_bits = 32,
> +        .reg_stride = 4,
> +        .val_bits = 32,
> +};
> 
> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>  {
> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> -	int errcode;
> -	int status;
> -
> -	if (ipcdev->irq) {
> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
> -						 CMD_TIMEOUT_SECONDS *
> HZ)) {
> -			dev_err(ipcdev->dev, "IPC timed out\n");
> -			return -ETIMEDOUT;
> -		}
> -	} else {
> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
> loops)
> -			udelay(1);
> -		if (!loops) {
> -			dev_err(ipcdev->dev, "IPC timed out\n");
> -			return -ETIMEDOUT;
> -		}
> -	}
> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
> +		return -EINVAL;
> 
> -	status = ipc_read_status(ipcdev, type);
> -	errcode = status & CMD_ERRCODE_MASK;
> -	if (errcode) {
> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
> -			ipc_err_string(errcode), status);
> -		return -EIO;
> -	}
> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
> +		cmd_list[2] << CMD_PARA1_SHIFT;
> 
>  	return 0;
>  }
> 
> -/**
> - * intel_punit_ipc_simple_command() - Simple IPC command
> - * @cmd:	IPC command code.
> - * @para1:	First 8bit parameter, set 0 if not used.
> - * @para2:	Second 8bit parameter, set 0 if not used.
> - *
> - * Send a IPC command to P-Unit when there is no data transaction
> - *
> - * Return:	IPC error code or 0 on success.
> - */
> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
> -	IPC_DEV *ipcdev = punit_ipcdev;
> -	IPC_TYPE type;
> -	u32 val;
> -	int ret;
> -
> -	mutex_lock(&ipcdev->lock);
> -
> -	reinit_completion(&ipcdev->cmd_complete);
> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> -
> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> CMD_PARA1_SHIFT;
> -	ipc_write_cmd(ipcdev, type, val);
> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> -
> -	mutex_unlock(&ipcdev->lock);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> -
> -/**
> - * intel_punit_ipc_command() - IPC command with data and pointers
> - * @cmd:	IPC command code.
> - * @para1:	First 8bit parameter, set 0 if not used.
> - * @para2:	Second 8bit parameter, set 0 if not used.
> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
> and ISPD.
> - * @out:	Output data.
> - *
> - * Send a IPC command to P-Unit with data transaction
> - *
> - * Return:	IPC error code or 0 on success.
> - */
> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> *out) -{
> -	IPC_DEV *ipcdev = punit_ipcdev;
> -	IPC_TYPE type;
> -	u32 val;
> -	int ret;
> -
> -	mutex_lock(&ipcdev->lock);
> -
> -	reinit_completion(&ipcdev->cmd_complete);
> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> -
> -	if (in) {
> -		ipc_write_data_low(ipcdev, type, *in);
> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> -			ipc_write_data_high(ipcdev, type, *++in);
> -	}
> -
> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> CMD_PARA1_SHIFT;
> -	ipc_write_cmd(ipcdev, type, val);
> -
> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> -	if (ret)
> -		goto out;
> -
> -	if (out) {
> -		*out = ipc_read_data_low(ipcdev, type);
> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> -			*++out = ipc_read_data_high(ipcdev, type);
> -	}
> -
> -out:
> -	mutex_unlock(&ipcdev->lock);
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> -
> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */ int
> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
> +		u32 outlen, u32 dptr, u32 sptr)
>  {
> -	IPC_DEV *ipcdev = dev_id;
> -
> -	complete(&ipcdev->cmd_complete);
> -	return IRQ_HANDLED;
> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
>  }
> 
>  static int intel_punit_get_bars(struct platform_device *pdev) @@ -282,9
> +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
>  	return 0;
>  }
> 
> +static int punit_ipc_err_code(int status) {
> +	return (status & CMD_ERRCODE_MASK);
> +}
> +
> +static int punit_ipc_busy_check(int status) {
> +	return status | CMD_RUN;
> +}
> +
> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
> +		const char *devname,
> +		int irq,
> +		void __iomem *base,
> +		void __iomem *data)
> +{
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +	struct regmap *cmd_regs, *data_regs;
> +
> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
> +        if (!cfg)
> +                return ERR_PTR(-ENOMEM);
> +
> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return ERR_PTR(-ENOMEM);
> +
> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> "%s_%s",
> +						  devname, "base");
> +
> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
> +			&punit_regmap_config);
> +	if (IS_ERR(cmd_regs)) {
> +                dev_err(dev, "cmd_regs regmap init failed\n");
> +                return ERR_CAST(cmd_regs);;
> +        }
> +
> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> "%s_%s",
> +						  devname, "data");
> +
> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
> +			&punit_regmap_config);
> +        if (IS_ERR(data_regs)) {
> +                dev_err(dev, "data_regs regmap init failed\n");
> +                return ERR_CAST(data_regs);;
> +        }
> +
> +	/* set IPC dev ops */
> +	ops->to_err_code = punit_ipc_err_code;
> +	ops->busy_check = punit_ipc_busy_check;
> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> +
> +	if (irq > 0)
> +	        cfg->mode = IPC_DEV_MODE_IRQ;
> +	else
> +	        cfg->mode = IPC_DEV_MODE_POLLING;
> +
> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
> +	cfg->irq = irq;
> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
> +	cfg->cmd_regs = cmd_regs;
> +	cfg->data_regs = data_regs;
> +
> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
> +
>  static int intel_punit_ipc_probe(struct platform_device *pdev)  {
> -	int irq, ret;
> +	int irq, ret, i;
> 
>  	punit_ipcdev = devm_kzalloc(&pdev->dev,
>  				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device
> *pdev)
>  	platform_set_drvdata(pdev, punit_ipcdev);
> 
>  	irq = platform_get_irq(pdev, 0);
> -	if (irq < 0) {
> -		punit_ipcdev->irq = 0;
> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
> -	} else {
> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
> -				       &punit_ipcdev);
> -		if (ret) {
> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
> irq);
> -			return ret;
> -		}
> -		punit_ipcdev->irq = irq;
> -	}
> 
>  	ret = intel_punit_get_bars(pdev);
>  	if (ret)
> -		goto out;
> +		return ret;
> +
> +	for (i = 0; i < RESERVED_IPC; i++) {
> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
> +				&pdev->dev,
> +				ipc_dev_name[i],
> +				irq,
> +				punit_ipcdev->base[i][BASE_IFACE],
> +				punit_ipcdev->base[i][BASE_DATA]);
> +
> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
> +			dev_err(&pdev->dev, "%s create failed\n",
> +					ipc_dev_name[i]);
> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
> +		}
> +	}
> 
>  	punit_ipcdev->dev = &pdev->dev;
> -	mutex_init(&punit_ipcdev->lock);
> -	init_completion(&punit_ipcdev->cmd_complete);
> 
> -out:
>  	return ret;
> -}
> 
> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
> -	return 0;
>  }
> 
>  static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@ -332,7 +250,6
> @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
> 
>  static struct platform_driver intel_punit_ipc_driver = {
>  	.probe = intel_punit_ipc_probe,
> -	.remove = intel_punit_ipc_remove,
>  	.driver = {
>  		.name = "intel_punit_ipc",
>  		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
> a/drivers/platform/x86/intel_telemetry_pltdrv.c
> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> index e0424d5..bf8284a 100644
> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c

This is a logical separation and so should be a different patch.

> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
> 
>  static struct telemetry_plt_config *telm_conf;
> +static struct intel_ipc_dev *punit_bios_ipc_dev;

Simply punit_ipc_dev is good enough.

> 
>  /*
>   * The following counters are programmed by default during setup.
> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
>  	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
>  };
> 
> -
>  static struct telemetry_evtmap
> 
> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
> NTS] = {
>  	{"IA_CORE0_C6_RES",			0x0400},
> @@ -283,13 +283,12 @@ static inline int
> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline int
> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
>  	u32 write_buf;
> -	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
> -				      index, 0, &write_buf, NULL);
> -
> -	return ret;
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
>  }

1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like
telemetry_punit_send_cmd(). Same thoughout

> 
>  static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
> evtconfig, @@ -435,6 +434,7 @@ static int
> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
>  	int ret, index, idx;
>  	u32 *pss_evtmap;
>  	u32 telem_ctrl;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	num_pss_evts = evtconfig.num_evts;
>  	pss_period = evtconfig.period;
> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* PSS Config */
>  	/* Get telemetry EVENT CTL */
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> -				      0, 0, NULL, &telem_ctrl);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN, NULL,
> +			0, &telem_ctrl, 1, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Read Failed\n");
>  		return ret;
> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* Disable Telemetry */
>  	TELEM_DISABLE(telem_ctrl);
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				      0, 0, &telem_ctrl, NULL);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
>  		return ret;
> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	TELEM_ENABLE_PERIODIC(telem_ctrl);
>  	telem_ctrl |= pss_period;
> 
> -	ret =
> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				      0, 0, &telem_ctrl, NULL);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
>  		return ret;
> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
> *pdev)  {
>  	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
>  	u32 read_buf, events, event_regs;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>  	int ret;
> 
>  	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
> telemetry_setup(struct platform_device *pdev)
>  	telm_conf->ioss_config.max_period =
> TELEM_MAX_PERIOD(read_buf);
> 
>  	/* PUNIT Mailbox Setup */
> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
> 0, 0,
> -				      NULL, &read_buf);
> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +			NULL, 0, &read_buf, 1, 0, 0);
>  	if (ret) {
>  		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
>  		return ret;
> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)  {
>  	u32 telem_ctrl = 0;
>  	int ret = 0;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	mutex_lock(&(telm_conf->telem_lock));
>  	if (ioss_period) {
> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		}
> 
>  		/* Get telemetry EVENT CTL */
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> -				0, 0, NULL, &telem_ctrl);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &telem_ctrl, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Read Failed\n");
>  			goto out;
> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
> 
>  		/* Disable Telemetry */
>  		TELEM_DISABLE(telem_ctrl);
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> +				0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			goto out;
> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		TELEM_ENABLE_PERIODIC(telem_ctrl);
>  		telem_ctrl |= pss_period;
> 
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> -				0, 0, &telem_ctrl, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> +				0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TELEM_CTRL Event Enable Write
> Failed\n");
>  			goto out;
> @@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum
> telemetry_unit telem_unit,  {
>  	u32 temp = 0;
>  	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	if (verbosity == NULL)
>  		return -EINVAL;
> @@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum
> telemetry_unit telem_unit,
>  	mutex_lock(&(telm_conf->telem_trace_lock));
>  	switch (telem_unit) {
>  	case TELEM_PSS:
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> -				0, 0, NULL, &temp);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Read Failed\n");
>  			goto out;
> @@ -1058,15 +1071,16 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
>  	u32 temp = 0;
>  	int ret;
> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> 
>  	verbosity &= TELEM_TRC_VERBOSITY_MASK;
> 
>  	mutex_lock(&(telm_conf->telem_trace_lock));
>  	switch (telem_unit) {
>  	case TELEM_PSS:
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> -				0, 0, NULL, &temp);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				NULL, 0, &temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Read Failed\n");
>  			goto out;
> @@ -1074,10 +1088,10 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> 
>  		TELEM_CLEAR_VERBOSITY_BITS(temp);
>  		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> -
> -		ret = intel_punit_ipc_command(
> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
> -				0, 0, &temp, NULL);
> +		punit_cmd_init(cmd,
> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> +				0, 0);
> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
>  			goto out;
> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
> platform_device *pdev)
>  	if (!id)
>  		return -ENODEV;
> 
> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
> +		return PTR_ERR(punit_bios_ipc_dev);
> +
>  	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> 
>  	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device
> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>  	telemetry_clear_pltdata();
> +	intel_ipc_dev_put(punit_bios_ipc_dev);
>  	iounmap(telm_conf->pss_config.regmap);
>  	iounmap(telm_conf->ioss_config.regmap);
> 
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
  2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
  (?)
@ 2017-10-09  5:18     ` Chakravarty, Souvik K
  -1 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  5:18 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC
> device calls
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Removed redundant IPC helper functions and refactored the driver to use
> generic IPC device driver APIs. Also, cleaned up the driver to minimize the
> usage of global variable ipcdev by propogating the struct intel_pmc_ipc_dev
> pointer or by getting it from device private data.
> 
> This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
> intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_pmc_ipc.h          |  37 +--
>  drivers/mfd/intel_soc_pmic_bxtwc.c            |  21 +-
>  drivers/platform/x86/intel_pmc_ipc.c          | 393 ++++++++++----------------
>  drivers/platform/x86/intel_telemetry_pltdrv.c | 119 ++++----
>  include/linux/mfd/intel_soc_pmic.h            |   2 +
>  5 files changed, 238 insertions(+), 334 deletions(-)
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Added unique name to PMC regmaps.
>  * Added support to clear interrupt bit.
>  * Added intel_ipc_dev_put() support.
> 
> Changes since v1:
>  * Removed custom APIs.
>  * Cleaned up PMC IPC user drivers to use APIs provided by generic
>    IPC driver.
> 
> diff --git a/arch/x86/include/asm/intel_pmc_ipc.h
> b/arch/x86/include/asm/intel_pmc_ipc.h
> index fac89eb..9fc7c3c 100644
> --- a/arch/x86/include/asm/intel_pmc_ipc.h
> +++ b/arch/x86/include/asm/intel_pmc_ipc.h
> @@ -1,10 +1,15 @@
>  #ifndef _ASM_X86_INTEL_PMC_IPC_H_
>  #define  _ASM_X86_INTEL_PMC_IPC_H_
> 
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +
> +#define INTEL_PMC_IPC_DEV		"intel_pmc_ipc"
> +#define PMC_PARAM_LEN			2
> +
>  /* Commands */
>  #define PMC_IPC_PMIC_ACCESS		0xFF
> -#define		PMC_IPC_PMIC_ACCESS_READ	0x0
> -#define		PMC_IPC_PMIC_ACCESS_WRITE	0x1
> +#define	PMC_IPC_PMIC_ACCESS_READ	0x0
> +#define	PMC_IPC_PMIC_ACCESS_WRITE	0x1
>  #define PMC_IPC_USB_PWR_CTRL		0xF0
>  #define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
>  #define PMC_IPC_PHY_CONFIG		0xEE
> @@ -28,13 +33,14 @@
>  #define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
>  #define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> 
> +static inline void pmc_cmd_init(u32 *cmd, u32 param1, u32 param2) {
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +}
> +
>  #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
> 
> -int intel_pmc_ipc_simple_command(int cmd, int sub); -int
> intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr);
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen);
>  int intel_pmc_s0ix_counter_read(u64 *data);  int intel_pmc_gcr_read(u32
> offset, u32 *data);  int intel_pmc_gcr_write(u32 offset, u32 data); @@ -42,23
> +48,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
> 
>  #else
> 
> -static inline int intel_pmc_ipc_simple_command(int cmd, int sub) -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr)
> -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen)
> -{
> -	return -EINVAL;
> -}
> -
>  static inline int intel_pmc_s0ix_counter_read(u64 *data)  {
>  	return -EINVAL;
> diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c
> b/drivers/mfd/intel_soc_pmic_bxtwc.c
> index 15bc052..f9df9e7 100644
> --- a/drivers/mfd/intel_soc_pmic_bxtwc.c
> +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
> @@ -271,6 +271,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
>  	u8 ipc_in[2];
>  	u8 ipc_out[4];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_READ};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -284,9 +286,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
> 
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_READ,
> -			ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), (u32 *)ipc_out, 1, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to read from PMIC\n");
>  		return ret;
> @@ -303,6 +304,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	int i2c_addr;
>  	u8 ipc_in[3];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_WRITE};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -317,9 +320,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
>  	ipc_in[2] = val;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_WRITE,
> -			ipc_in, sizeof(ipc_in), NULL, 0);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), NULL, 0, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to write to PMIC\n");
>  		return ret;
> @@ -445,6 +447,10 @@ static int bxtwc_probe(struct platform_device
> *pdev)
>  	if (!pmic)
>  		return -ENOMEM;
> 
> +	pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmic->ipc_dev))
> +		return PTR_ERR(pmic->ipc_dev);
> +
>  	ret = platform_get_irq(pdev, 0);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "Invalid IRQ\n"); @@ -562,7 +568,10
> @@ static int bxtwc_probe(struct platform_device *pdev)
> 
>  static int bxtwc_remove(struct platform_device *pdev)  {
> +	struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
> +
>  	sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
> +	intel_ipc_dev_put(pmic->ipc_dev);
> 
>  	return 0;
>  }
> diff --git a/drivers/platform/x86/intel_pmc_ipc.c
> b/drivers/platform/x86/intel_pmc_ipc.c
> index fd5bcdf..35d4734 100644
> --- a/drivers/platform/x86/intel_pmc_ipc.c
> +++ b/drivers/platform/x86/intel_pmc_ipc.c
> @@ -47,18 +47,8 @@
>   * The ARC handles the interrupt and services it, writing optional data to
>   * the IPC1 registers, updates the IPC_STS response register with the status.
>   */
> -#define IPC_CMD			0x0
> -#define		IPC_CMD_MSI		0x100
>  #define		IPC_CMD_SIZE		16
>  #define		IPC_CMD_SUBCMD		12
> -#define IPC_STATUS		0x04
> -#define		IPC_STATUS_IRQ		0x4
> -#define		IPC_STATUS_ERR		0x2
> -#define		IPC_STATUS_BUSY		0x1
> -#define IPC_SPTR		0x08
> -#define IPC_DPTR		0x0C
> -#define IPC_WRITE_BUFFER	0x80
> -#define IPC_READ_BUFFER		0x90
> 
>  /* Residency with clock rate at 19.2MHz to usecs */
>  #define S0IX_RESIDENCY_IN_USECS(d, s)		\
> @@ -73,11 +63,6 @@
>   */
>  #define IPC_DATA_BUFFER_SIZE	16
> 
> -#define IPC_LOOP_CNT		3000000
> -#define IPC_MAX_SEC		3
> -
> -#define IPC_TRIGGER_MODE_IRQ		true
> -
>  /* exported resources from IFWI */
>  #define PLAT_RESOURCE_IPC_INDEX		0
>  #define PLAT_RESOURCE_IPC_SIZE		0x1000
> @@ -117,36 +102,41 @@
>  #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
>  #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
> 
> +/* IPC PMC commands */
> +#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
> +#define	IPC_DEV_PMC_CMD_SIZE			16
> +#define	IPC_DEV_PMC_CMD_SUBCMD			12
> +#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK
> 	GENMASK(7, 0)
> +#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
> +
> +/*IPC PMC reg offsets */
> +#define IPC_DEV_PMC_STATUS_OFFSET		0x04
> +#define IPC_DEV_PMC_SPTR_OFFSET			0x08
> +#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
> +#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
> +#define IPC_DEV_PMC_RBUF_OFFSET			0x90
> +
>  static struct intel_pmc_ipc_dev {
>  	struct device *dev;
> +	struct intel_ipc_dev *pmc_ipc_dev;
> +	struct intel_ipc_dev_ops ops;
> +	struct intel_ipc_dev_cfg cfg;
>  	void __iomem *ipc_base;
> -	bool irq_mode;
> -	int irq;
> -	int cmd;
> -	struct completion cmd_complete;
> 
>  	/* gcr */
>  	void __iomem *gcr_mem_base;
>  	struct regmap *gcr_regs;
>  } ipcdev;
> 
> -static char *ipc_err_sources[] = {
> -	[IPC_ERR_NONE] =
> -		"no error",
> -	[IPC_ERR_CMD_NOT_SUPPORTED] =
> -		"command not supported",
> -	[IPC_ERR_CMD_NOT_SERVICED] =
> -		"command not serviced",
> -	[IPC_ERR_UNABLE_TO_SERVICE] =
> -		"unable to service",
> -	[IPC_ERR_CMD_INVALID] =
> -		"command invalid",
> -	[IPC_ERR_CMD_FAILED] =
> -		"command failed",
> -	[IPC_ERR_EMSECURITY] =
> -		"Invalid Battery",
> -	[IPC_ERR_UNSIGNEDKERNEL] =
> -		"Unsigned kernel",
> +static struct regmap_config pmc_regmap_config = {
> +	.name = "intel_pmc_regs",
> +        .reg_bits = 32,
> +        .reg_stride = 4,
> +        .val_bits = 32,
> +	.fast_io = true,
>  };
> 
>  static struct regmap_config gcr_regmap_config = { @@ -158,40 +148,6 @@
> static struct regmap_config gcr_regmap_config = {
>  	.max_register = PLAT_RESOURCE_GCR_SIZE,  };
> 
> -/* Prevent concurrent calls to the PMC */ -static DEFINE_MUTEX(ipclock);
> -
> -static inline void ipc_send_command(u32 cmd) -{
> -	ipcdev.cmd = cmd;
> -	if (ipcdev.irq_mode) {
> -		reinit_completion(&ipcdev.cmd_complete);
> -		cmd |= IPC_CMD_MSI;
> -	}
> -	writel(cmd, ipcdev.ipc_base + IPC_CMD);
> -}
> -
> -static inline u32 ipc_read_status(void) -{
> -	return readl(ipcdev.ipc_base + IPC_STATUS);
> -}
> -
> -static inline void ipc_data_writel(u32 data, u32 offset) -{
> -	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
> -}
> -
> -static inline u8 __maybe_unused ipc_data_readb(u32 offset) -{
> -	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -static inline u32 ipc_data_readl(u32 offset) -{
> -	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -
>  /**
>   * intel_pmc_gcr_read() - Read PMC GCR register
>   * @offset:	offset of GCR register from GCR address base
> @@ -263,160 +219,109 @@ static int update_no_reboot_bit(void *priv, bool
> set)
>  				    PMC_CFG_NO_REBOOT_MASK, value);  }
> 
> -static int intel_pmc_ipc_check_status(void)
> +static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>  {
> -	int status;
> -	int ret = 0;
> -
> -	if (ipcdev.irq_mode) {
> -		if (0 == wait_for_completion_timeout(
> -				&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
> -			ret = -ETIMEDOUT;
> -	} else {
> -		int loop_count = IPC_LOOP_CNT;
> -
> -		while ((ipc_read_status() & IPC_STATUS_BUSY) && --
> loop_count)
> -			udelay(1);
> -		if (loop_count == 0)
> -			ret = -ETIMEDOUT;
> -	}
> -
> -	status = ipc_read_status();
> -	if (ret == -ETIMEDOUT) {
> -		dev_err(ipcdev.dev,
> -			"IPC timed out, TS=0x%x, CMD=0x%x\n",
> -			status, ipcdev.cmd);
> -		return ret;
> -	}
> +	if (!cmd_list || cmdlen != PMC_PARAM_LEN)
> +		return -EINVAL;
> 
> -	if (status & IPC_STATUS_ERR) {
> -		int i;
> -
> -		ret = -EIO;
> -		i = (status >> IPC_CMD_SIZE) & 0xFF;
> -		if (i < ARRAY_SIZE(ipc_err_sources))
> -			dev_err(ipcdev.dev,
> -				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> -				ipc_err_sources[i], status, ipcdev.cmd);
> -		else
> -			dev_err(ipcdev.dev,
> -				"IPC failed: unknown, STS=0x%x,
> CMD=0x%x\n",
> -				status, ipcdev.cmd);
> -		if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i ==
> IPC_ERR_EMSECURITY))
> -			ret = -EACCES;
> -	}
> +	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
> 
> -	return ret;
> +	return 0;
>  }
> 
> -/**
> - * intel_pmc_ipc_simple_command() - Simple IPC command
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - *
> - * Send a simple IPC command to PMC when don't need to specify
> - * input/output data and source/dest pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_simple_command(int cmd, int sub)
> +static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +		u32 *out, u32 outlen, u32 dptr, u32 sptr)
>  {
>  	int ret;
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	mutex_unlock(&ipclock);
> +	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE/4)
> +		return -EINVAL;
> 
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
> +	if (ret < 0)
> +		return ret;
> 
> -/**
> - * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - * @sptr:	data writing to SPTR register.
> - * @dptr:	data writing to DPTR register.
> - *
> - * Send an IPC command to PMC with input/output data and source/dest
> pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
> -			  u32 outlen, u32 dptr, u32 sptr)
> -{
> -	u32 wbuf[4] = { 0 };
> -	int ret;
> -	int i;
> +	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
> 
> -	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE / 4)
> -		return -EINVAL;
> +	return 0;
> +}
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	memcpy(wbuf, in, inlen);
> -	writel(dptr, ipcdev.ipc_base + IPC_DPTR);
> -	writel(sptr, ipcdev.ipc_base + IPC_SPTR);
> -	/* The input data register is 32bit register and inlen is in Byte */
> -	for (i = 0; i < ((inlen + 3) / 4); i++)
> -		ipc_data_writel(wbuf[i], 4 * i);
> -	ipc_send_command((inlen << IPC_CMD_SIZE) |
> -			(sub << IPC_CMD_SUBCMD) | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	if (!ret) {
> -		/* out is read from 32bit register and outlen is in 32bit */
> -		for (i = 0; i < outlen; i++)
> -			*out++ = ipc_data_readl(4 * i);
> -	}
> -	mutex_unlock(&ipclock);
> +static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq) {
> +	return regmap_write_bits(ipc_dev->cfg->cmd_regs,
> +				  ipc_dev->cfg->status_reg,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ);
> +}
> 
> -	return ret;
> +static int pmc_ipc_err_code(int status) {
> +	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
> +			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
> 
> -/**
> - * intel_pmc_ipc_command() -  IPC command with input/output data
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - *
> - * Send an IPC command to PMC with input/output data.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -			  u32 *out, u32 outlen)
> +static int pmc_ipc_busy_check(int status)
>  {
> -	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
> +	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
> 
> -static irqreturn_t ioc(int irq, void *dev_id)
> +static u32 pmc_ipc_enable_msi(u32 cmd)
>  {
> -	int status;
> +	return cmd | IPC_DEV_PMC_CMD_MSI;
> +}
> 
> -	if (ipcdev.irq_mode) {
> -		status = ipc_read_status();
> -		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base +
> IPC_STATUS);
> -	}
> -	complete(&ipcdev.cmd_complete);
> +static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
> +		struct device *pmc_dev,
> +		void __iomem *base,
> +		int irq)
> +{
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +	struct regmap *cmd_regs;
> +
> +	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
> +	if (!cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return ERR_PTR(-ENOMEM);
> +
> +        cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
> +			&pmc_regmap_config);
> +        if (IS_ERR(cmd_regs)) {
> +                dev_err(pmc_dev, "cmd_regs regmap init failed\n");
> +                return ERR_CAST(cmd_regs);;
> +        }
> 
> -	return IRQ_HANDLED;
> +	/* set IPC dev ops */
> +	ops->to_err_code = pmc_ipc_err_code;
> +	ops->busy_check = pmc_ipc_busy_check;
> +	ops->enable_msi = pmc_ipc_enable_msi;
> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> +	ops->pre_irq_handler_fn = pre_irq_handler_fn;
> +
> +	/* set cfg options */
> +	if (irq > 0)
> +		cfg->mode = IPC_DEV_MODE_IRQ;
> +	else
> +		cfg->mode = IPC_DEV_MODE_POLLING;
> +
> +	cfg->chan_type = IPC_CHANNEL_IA_PMC;
> +	cfg->irq = irq;
> +	cfg->use_msi = true;
> +	cfg->support_sptr = true;
> +	cfg->support_dptr = true;
> +	cfg->cmd_regs = cmd_regs;
> +	cfg->data_regs = cmd_regs;
> +	cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
> +	cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
> +	cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
> +	cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
> +	cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
> +
> +	return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV,
> cfg,
> +ops);
>  }
> 
>  static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> @@ -428,8 +333,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const
> struct pci_device_id *id)
>  	if (pmc->dev)
>  		return -EBUSY;
> 
> -	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
> -
>  	ret = pcim_enable_device(pdev);
>  	if (ret)
>  		return ret;
> @@ -438,15 +341,14 @@ static int ipc_pci_probe(struct pci_dev *pdev,
> const struct pci_device_id *id)
>  	if (ret)
>  		return ret;
> 
> -	init_completion(&pmc->cmd_complete);
> -
>  	pmc->ipc_base = pcim_iomap_table(pdev)[0];
> 
> -	ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0,
> "intel_pmc_ipc",
> -				pmc);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return ret;
> +	pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, pdev->irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev,
> +				"Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
>  	}
> 
>  	pmc->dev = &pdev->dev;
> @@ -474,19 +376,19 @@ static ssize_t
> intel_pmc_ipc_simple_cmd_store(struct device *dev,
>  					      struct device_attribute *attr,
>  					      const char *buf, size_t count)  {
> -	int subcmd;
> -	int cmd;
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
> +	int cmd[2];
>  	int ret;
> 
> -	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
> +	ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
>  	if (ret != 2) {
>  		dev_err(dev, "Error args\n");
>  		return -EINVAL;
>  	}
> 
> -	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command %d error with %d\n", cmd, ret);
> +		dev_err(dev, "command %d error with %d\n", cmd[0], ret);
>  		return ret;
>  	}
>  	return (ssize_t)count;
> @@ -496,22 +398,23 @@ static ssize_t
> intel_pmc_ipc_northpeak_store(struct device *dev,
>  					     struct device_attribute *attr,
>  					     const char *buf, size_t count)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
>  	unsigned long val;
> -	int subcmd;
> +	int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
>  	int ret;
> 
>  	if (kstrtoul(buf, 0, &val))
>  		return -EINVAL;
> 
>  	if (val)
> -		subcmd = 1;
> -	else
> -		subcmd = 0;
> -	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL,
> subcmd);
> +		cmd[1] = 1;
> +
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command north %d error with %d\n", subcmd,
> ret);
> +		dev_err(dev, "command north %d error with %d\n", cmd[1],
> ret);
>  		return ret;
>  	}
> +
>  	return (ssize_t)count;
>  }
> 
> @@ -689,6 +592,7 @@ static int ipc_create_pmc_devices(struct
> platform_device *pdev)
> 
>  static int ipc_plat_get_res(struct platform_device *pdev)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
>  	struct resource *res;
>  	void __iomem *addr;
> 
> @@ -707,9 +611,9 @@ static int ipc_plat_get_res(struct platform_device
> *pdev)
>  	if (IS_ERR(addr))
>  		return PTR_ERR(addr);
> 
> -	ipcdev.ipc_base = addr;
> -	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> -	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
> +	pmc->ipc_base = addr;
> +	pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> +	dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
> 
>  	return 0;
>  }
> @@ -755,14 +659,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
> 
>  static int ipc_plat_probe(struct platform_device *pdev)  {
> -	int ret;
> +	int ret, irq;
> +	struct intel_pmc_ipc_dev *pmc = &ipcdev;
> +
> +	pmc->dev = &pdev->dev;
> 
> -	ipcdev.dev = &pdev->dev;
> -	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
> -	init_completion(&ipcdev.cmd_complete);
> +	dev_set_drvdata(&pdev->dev, pmc);
> 
> -	ipcdev.irq = platform_get_irq(pdev, 0);
> -	if (ipcdev.irq < 0) {
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
>  		dev_err(&pdev->dev, "Failed to get irq\n");
>  		return -EINVAL;
>  	}
> @@ -773,11 +678,11 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
> -			ipcdev.gcr_mem_base, &gcr_regmap_config);
> -        if (IS_ERR(ipcdev.gcr_regs)) {
> -                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
> -                return PTR_ERR(ipcdev.gcr_regs);;
> +        pmc->gcr_regs = devm_regmap_init_mmio_clk(pmc->dev, NULL,
> +			pmc->gcr_mem_base, &gcr_regmap_config);
> +        if (IS_ERR(pmc->gcr_regs)) {
> +                dev_err(&pdev->dev, "gcr_regs regmap init failed\n");
> +                return PTR_ERR(pmc->gcr_regs);;

Extra semicolon on preceding line.

>          }
> 
>  	ret = ipc_create_pmc_devices(pdev);
> @@ -786,20 +691,20 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,
> IRQF_NO_SUSPEND,
> -			     "intel_pmc_ipc", &ipcdev)) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return -EBUSY;
> -	}
> -
>  	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
>  			ret);
> -		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
>  		return ret;
>  	}
> 
> +	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
> +	}
> +
>  	return 0;
>  }
> 

This part below can logically be a separate patch (alongwith intel_soc_pmic_bxtwc.c file too)

> diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c
> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> index bf8284a..a08a620 100644
> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> @@ -56,10 +56,6 @@
>  #define IOSS_TELEM_TRACE_CTL_WRITE	0x6
>  #define IOSS_TELEM_EVENT_CTL_READ	0x7
>  #define IOSS_TELEM_EVENT_CTL_WRITE	0x8
> -#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE	0x4
> -#define IOSS_TELEM_READ_WORD		0x1
> -#define IOSS_TELEM_WRITE_FOURBYTES	0x4
> -#define IOSS_TELEM_EVT_WRITE_SIZE	0x3
> 
>  #define TELEM_INFO_SRAMEVTS_MASK	0xFF00
>  #define TELEM_INFO_SRAMEVTS_SHIFT	0x8
> @@ -98,7 +94,7 @@ struct telem_ssram_region {  };
> 
>  static struct telemetry_plt_config *telm_conf; -static struct intel_ipc_dev
> *punit_bios_ipc_dev;
> +static struct intel_ipc_dev *punit_bios_ipc_dev, *pmc_ipc_dev;
> 
>  /*
>   * The following counters are programmed by default during setup.
> @@ -267,17 +263,16 @@ static int telemetry_check_evtid(enum
> telemetry_unit telem_unit,  static inline int
> telemetry_plt_config_ioss_event(u32 evt_id, int index)  {
>  	u32 write_buf;
> -	int ret;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	write_buf <<= BITS_PER_BYTE;
>  	write_buf |= index;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_WRITE, (u8
> *)&write_buf,
> -				    IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_WRITE);
> 
> -	return ret;
> +	return ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);

1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
2) pmc_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like telemetry_pmc_send_cmd(). Same thoughout.

>  }
> 
>  static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) @@ -
> 287,6 +282,7 @@ static inline int telemetry_plt_config_pss_event(u32
> evt_id, int index)
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> +
>  	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
>  			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);  }
> @@ -298,15 +294,16 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	int ret, index, idx;
>  	u32 *ioss_evtmap;
>  	u32 telem_ctrl;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	num_ioss_evts = evtconfig.num_evts;
>  	ioss_period = evtconfig.period;
>  	ioss_evtmap = evtconfig.evtmap;
> 
>  	/* Get telemetry EVENT CTL */
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
> -				    &telem_ctrl, IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&telem_ctrl, 1, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Read Failed\n");
>  		return ret;
> @@ -314,12 +311,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* Disable Telemetry */
>  	TELEM_DISABLE(telem_ctrl);
> -
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -				    NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
>  		return ret;
> @@ -330,12 +324,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	if (action == TELEM_RESET) {
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl),
> +				NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -360,11 +353,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -412,10 +405,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	TELEM_ENABLE_PERIODIC(telem_ctrl);
>  	telem_ctrl |= ioss_period;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL,
> 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
>  		return ret;
> @@ -609,8 +601,9 @@ static int telemetry_setup(struct platform_device
> *pdev)
>  	u32 cmd[PUNIT_PARAM_LEN] = {0};
>  	int ret;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ,
> -				    NULL, 0, &read_buf,
> IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&read_buf, 1, 0, 0);
>  	if (ret) {
>  		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
>  		return ret;
> @@ -713,9 +706,10 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		}
> 
>  		/* Get telemetry EVENT CTL */
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_READ,
> NULL, 0,
> -					    &telem_ctrl,
> IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&telem_ctrl, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Read Failed\n");
>  			goto out;
> @@ -723,12 +717,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
> 
>  		/* Disable Telemetry */
>  		TELEM_DISABLE(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			goto out;
> @@ -740,11 +733,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		TELEM_ENABLE_PERIODIC(telem_ctrl);
>  		telem_ctrl |= ioss_period;
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Enable Write
> Failed\n");
>  			goto out;
> @@ -1044,9 +1037,10 @@ static int
> telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1099,9 +1093,10 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1109,10 +1104,9 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> 
>  		TELEM_CLEAR_VERBOSITY_BITS(temp);
>  		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_WRITE, (u8
> *)&temp,
> -				IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_TRACE_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
>  			goto out;
> @@ -1157,6 +1151,10 @@ static int telemetry_pltdrv_probe(struct
> platform_device *pdev)
>  	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
>  		return PTR_ERR(punit_bios_ipc_dev);
> 
> +	pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmc_ipc_dev))
> +		return PTR_ERR(pmc_ipc_dev);
> +
>  	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> 
>  	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> 1236,6 +1234,7 @@ static int telemetry_pltdrv_probe(struct platform_device
> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>  	telemetry_clear_pltdata();
> +	intel_ipc_dev_put(pmc_ipc_dev);
>  	intel_ipc_dev_put(punit_bios_ipc_dev);
>  	iounmap(telm_conf->pss_config.regmap);
>  	iounmap(telm_conf->ioss_config.regmap);
> diff --git a/include/linux/mfd/intel_soc_pmic.h
> b/include/linux/mfd/intel_soc_pmic.h
> index 5aacdb0..7cc39b6 100644
> --- a/include/linux/mfd/intel_soc_pmic.h
> +++ b/include/linux/mfd/intel_soc_pmic.h
> @@ -20,6 +20,7 @@
>  #define __INTEL_SOC_PMIC_H__
> 
>  #include <linux/regmap.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> 
>  struct intel_soc_pmic {
>  	int irq;
> @@ -31,6 +32,7 @@ struct intel_soc_pmic {
>  	struct regmap_irq_chip_data *irq_chip_data_chgr;
>  	struct regmap_irq_chip_data *irq_chip_data_crit;
>  	struct device *dev;
> +	struct intel_ipc_dev *ipc_dev;
>  };
> 
>  #endif	/* __INTEL_SOC_PMIC_H__ */
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
@ 2017-10-09  5:18     ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  5:18 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

> From: sathyanarayanan.kuppuswamy@linux.intel.com
> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
> Souvik K <souvik.k.chakravarty@intel.com>
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> Subject: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC
> device calls
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> Removed redundant IPC helper functions and refactored the driver to use
> generic IPC device driver APIs. Also, cleaned up the driver to minimize the
> usage of global variable ipcdev by propogating the struct intel_pmc_ipc_dev
> pointer or by getting it from device private data.
> 
> This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
> intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_pmc_ipc.h          |  37 +--
>  drivers/mfd/intel_soc_pmic_bxtwc.c            |  21 +-
>  drivers/platform/x86/intel_pmc_ipc.c          | 393 ++++++++++----------------
>  drivers/platform/x86/intel_telemetry_pltdrv.c | 119 ++++----
>  include/linux/mfd/intel_soc_pmic.h            |   2 +
>  5 files changed, 238 insertions(+), 334 deletions(-)
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Added unique name to PMC regmaps.
>  * Added support to clear interrupt bit.
>  * Added intel_ipc_dev_put() support.
> 
> Changes since v1:
>  * Removed custom APIs.
>  * Cleaned up PMC IPC user drivers to use APIs provided by generic
>    IPC driver.
> 
> diff --git a/arch/x86/include/asm/intel_pmc_ipc.h
> b/arch/x86/include/asm/intel_pmc_ipc.h
> index fac89eb..9fc7c3c 100644
> --- a/arch/x86/include/asm/intel_pmc_ipc.h
> +++ b/arch/x86/include/asm/intel_pmc_ipc.h
> @@ -1,10 +1,15 @@
>  #ifndef _ASM_X86_INTEL_PMC_IPC_H_
>  #define  _ASM_X86_INTEL_PMC_IPC_H_
> 
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +
> +#define INTEL_PMC_IPC_DEV		"intel_pmc_ipc"
> +#define PMC_PARAM_LEN			2
> +
>  /* Commands */
>  #define PMC_IPC_PMIC_ACCESS		0xFF
> -#define		PMC_IPC_PMIC_ACCESS_READ	0x0
> -#define		PMC_IPC_PMIC_ACCESS_WRITE	0x1
> +#define	PMC_IPC_PMIC_ACCESS_READ	0x0
> +#define	PMC_IPC_PMIC_ACCESS_WRITE	0x1
>  #define PMC_IPC_USB_PWR_CTRL		0xF0
>  #define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
>  #define PMC_IPC_PHY_CONFIG		0xEE
> @@ -28,13 +33,14 @@
>  #define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
>  #define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> 
> +static inline void pmc_cmd_init(u32 *cmd, u32 param1, u32 param2) {
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +}
> +
>  #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
> 
> -int intel_pmc_ipc_simple_command(int cmd, int sub); -int
> intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr);
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen);
>  int intel_pmc_s0ix_counter_read(u64 *data);  int intel_pmc_gcr_read(u32
> offset, u32 *data);  int intel_pmc_gcr_write(u32 offset, u32 data); @@ -42,23
> +48,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
> 
>  #else
> 
> -static inline int intel_pmc_ipc_simple_command(int cmd, int sub) -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr)
> -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen)
> -{
> -	return -EINVAL;
> -}
> -
>  static inline int intel_pmc_s0ix_counter_read(u64 *data)  {
>  	return -EINVAL;
> diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c
> b/drivers/mfd/intel_soc_pmic_bxtwc.c
> index 15bc052..f9df9e7 100644
> --- a/drivers/mfd/intel_soc_pmic_bxtwc.c
> +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
> @@ -271,6 +271,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
>  	u8 ipc_in[2];
>  	u8 ipc_out[4];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_READ};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -284,9 +286,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
> 
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_READ,
> -			ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), (u32 *)ipc_out, 1, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to read from PMIC\n");
>  		return ret;
> @@ -303,6 +304,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	int i2c_addr;
>  	u8 ipc_in[3];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_WRITE};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -317,9 +320,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
>  	ipc_in[2] = val;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_WRITE,
> -			ipc_in, sizeof(ipc_in), NULL, 0);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), NULL, 0, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to write to PMIC\n");
>  		return ret;
> @@ -445,6 +447,10 @@ static int bxtwc_probe(struct platform_device
> *pdev)
>  	if (!pmic)
>  		return -ENOMEM;
> 
> +	pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmic->ipc_dev))
> +		return PTR_ERR(pmic->ipc_dev);
> +
>  	ret = platform_get_irq(pdev, 0);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "Invalid IRQ\n"); @@ -562,7 +568,10
> @@ static int bxtwc_probe(struct platform_device *pdev)
> 
>  static int bxtwc_remove(struct platform_device *pdev)  {
> +	struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
> +
>  	sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
> +	intel_ipc_dev_put(pmic->ipc_dev);
> 
>  	return 0;
>  }
> diff --git a/drivers/platform/x86/intel_pmc_ipc.c
> b/drivers/platform/x86/intel_pmc_ipc.c
> index fd5bcdf..35d4734 100644
> --- a/drivers/platform/x86/intel_pmc_ipc.c
> +++ b/drivers/platform/x86/intel_pmc_ipc.c
> @@ -47,18 +47,8 @@
>   * The ARC handles the interrupt and services it, writing optional data to
>   * the IPC1 registers, updates the IPC_STS response register with the status.
>   */
> -#define IPC_CMD			0x0
> -#define		IPC_CMD_MSI		0x100
>  #define		IPC_CMD_SIZE		16
>  #define		IPC_CMD_SUBCMD		12
> -#define IPC_STATUS		0x04
> -#define		IPC_STATUS_IRQ		0x4
> -#define		IPC_STATUS_ERR		0x2
> -#define		IPC_STATUS_BUSY		0x1
> -#define IPC_SPTR		0x08
> -#define IPC_DPTR		0x0C
> -#define IPC_WRITE_BUFFER	0x80
> -#define IPC_READ_BUFFER		0x90
> 
>  /* Residency with clock rate at 19.2MHz to usecs */
>  #define S0IX_RESIDENCY_IN_USECS(d, s)		\
> @@ -73,11 +63,6 @@
>   */
>  #define IPC_DATA_BUFFER_SIZE	16
> 
> -#define IPC_LOOP_CNT		3000000
> -#define IPC_MAX_SEC		3
> -
> -#define IPC_TRIGGER_MODE_IRQ		true
> -
>  /* exported resources from IFWI */
>  #define PLAT_RESOURCE_IPC_INDEX		0
>  #define PLAT_RESOURCE_IPC_SIZE		0x1000
> @@ -117,36 +102,41 @@
>  #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
>  #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
> 
> +/* IPC PMC commands */
> +#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
> +#define	IPC_DEV_PMC_CMD_SIZE			16
> +#define	IPC_DEV_PMC_CMD_SUBCMD			12
> +#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK
> 	GENMASK(7, 0)
> +#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
> +
> +/*IPC PMC reg offsets */
> +#define IPC_DEV_PMC_STATUS_OFFSET		0x04
> +#define IPC_DEV_PMC_SPTR_OFFSET			0x08
> +#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
> +#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
> +#define IPC_DEV_PMC_RBUF_OFFSET			0x90
> +
>  static struct intel_pmc_ipc_dev {
>  	struct device *dev;
> +	struct intel_ipc_dev *pmc_ipc_dev;
> +	struct intel_ipc_dev_ops ops;
> +	struct intel_ipc_dev_cfg cfg;
>  	void __iomem *ipc_base;
> -	bool irq_mode;
> -	int irq;
> -	int cmd;
> -	struct completion cmd_complete;
> 
>  	/* gcr */
>  	void __iomem *gcr_mem_base;
>  	struct regmap *gcr_regs;
>  } ipcdev;
> 
> -static char *ipc_err_sources[] = {
> -	[IPC_ERR_NONE] =
> -		"no error",
> -	[IPC_ERR_CMD_NOT_SUPPORTED] =
> -		"command not supported",
> -	[IPC_ERR_CMD_NOT_SERVICED] =
> -		"command not serviced",
> -	[IPC_ERR_UNABLE_TO_SERVICE] =
> -		"unable to service",
> -	[IPC_ERR_CMD_INVALID] =
> -		"command invalid",
> -	[IPC_ERR_CMD_FAILED] =
> -		"command failed",
> -	[IPC_ERR_EMSECURITY] =
> -		"Invalid Battery",
> -	[IPC_ERR_UNSIGNEDKERNEL] =
> -		"Unsigned kernel",
> +static struct regmap_config pmc_regmap_config = {
> +	.name = "intel_pmc_regs",
> +        .reg_bits = 32,
> +        .reg_stride = 4,
> +        .val_bits = 32,
> +	.fast_io = true,
>  };
> 
>  static struct regmap_config gcr_regmap_config = { @@ -158,40 +148,6 @@
> static struct regmap_config gcr_regmap_config = {
>  	.max_register = PLAT_RESOURCE_GCR_SIZE,  };
> 
> -/* Prevent concurrent calls to the PMC */ -static DEFINE_MUTEX(ipclock);
> -
> -static inline void ipc_send_command(u32 cmd) -{
> -	ipcdev.cmd = cmd;
> -	if (ipcdev.irq_mode) {
> -		reinit_completion(&ipcdev.cmd_complete);
> -		cmd |= IPC_CMD_MSI;
> -	}
> -	writel(cmd, ipcdev.ipc_base + IPC_CMD);
> -}
> -
> -static inline u32 ipc_read_status(void) -{
> -	return readl(ipcdev.ipc_base + IPC_STATUS);
> -}
> -
> -static inline void ipc_data_writel(u32 data, u32 offset) -{
> -	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
> -}
> -
> -static inline u8 __maybe_unused ipc_data_readb(u32 offset) -{
> -	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -static inline u32 ipc_data_readl(u32 offset) -{
> -	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -
>  /**
>   * intel_pmc_gcr_read() - Read PMC GCR register
>   * @offset:	offset of GCR register from GCR address base
> @@ -263,160 +219,109 @@ static int update_no_reboot_bit(void *priv, bool
> set)
>  				    PMC_CFG_NO_REBOOT_MASK, value);  }
> 
> -static int intel_pmc_ipc_check_status(void)
> +static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>  {
> -	int status;
> -	int ret = 0;
> -
> -	if (ipcdev.irq_mode) {
> -		if (0 == wait_for_completion_timeout(
> -				&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
> -			ret = -ETIMEDOUT;
> -	} else {
> -		int loop_count = IPC_LOOP_CNT;
> -
> -		while ((ipc_read_status() & IPC_STATUS_BUSY) && --
> loop_count)
> -			udelay(1);
> -		if (loop_count == 0)
> -			ret = -ETIMEDOUT;
> -	}
> -
> -	status = ipc_read_status();
> -	if (ret == -ETIMEDOUT) {
> -		dev_err(ipcdev.dev,
> -			"IPC timed out, TS=0x%x, CMD=0x%x\n",
> -			status, ipcdev.cmd);
> -		return ret;
> -	}
> +	if (!cmd_list || cmdlen != PMC_PARAM_LEN)
> +		return -EINVAL;
> 
> -	if (status & IPC_STATUS_ERR) {
> -		int i;
> -
> -		ret = -EIO;
> -		i = (status >> IPC_CMD_SIZE) & 0xFF;
> -		if (i < ARRAY_SIZE(ipc_err_sources))
> -			dev_err(ipcdev.dev,
> -				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> -				ipc_err_sources[i], status, ipcdev.cmd);
> -		else
> -			dev_err(ipcdev.dev,
> -				"IPC failed: unknown, STS=0x%x,
> CMD=0x%x\n",
> -				status, ipcdev.cmd);
> -		if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i ==
> IPC_ERR_EMSECURITY))
> -			ret = -EACCES;
> -	}
> +	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
> 
> -	return ret;
> +	return 0;
>  }
> 
> -/**
> - * intel_pmc_ipc_simple_command() - Simple IPC command
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - *
> - * Send a simple IPC command to PMC when don't need to specify
> - * input/output data and source/dest pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_simple_command(int cmd, int sub)
> +static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +		u32 *out, u32 outlen, u32 dptr, u32 sptr)
>  {
>  	int ret;
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	mutex_unlock(&ipclock);
> +	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE/4)
> +		return -EINVAL;
> 
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
> +	if (ret < 0)
> +		return ret;
> 
> -/**
> - * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - * @sptr:	data writing to SPTR register.
> - * @dptr:	data writing to DPTR register.
> - *
> - * Send an IPC command to PMC with input/output data and source/dest
> pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
> -			  u32 outlen, u32 dptr, u32 sptr)
> -{
> -	u32 wbuf[4] = { 0 };
> -	int ret;
> -	int i;
> +	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
> 
> -	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE / 4)
> -		return -EINVAL;
> +	return 0;
> +}
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	memcpy(wbuf, in, inlen);
> -	writel(dptr, ipcdev.ipc_base + IPC_DPTR);
> -	writel(sptr, ipcdev.ipc_base + IPC_SPTR);
> -	/* The input data register is 32bit register and inlen is in Byte */
> -	for (i = 0; i < ((inlen + 3) / 4); i++)
> -		ipc_data_writel(wbuf[i], 4 * i);
> -	ipc_send_command((inlen << IPC_CMD_SIZE) |
> -			(sub << IPC_CMD_SUBCMD) | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	if (!ret) {
> -		/* out is read from 32bit register and outlen is in 32bit */
> -		for (i = 0; i < outlen; i++)
> -			*out++ = ipc_data_readl(4 * i);
> -	}
> -	mutex_unlock(&ipclock);
> +static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq) {
> +	return regmap_write_bits(ipc_dev->cfg->cmd_regs,
> +				  ipc_dev->cfg->status_reg,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ);
> +}
> 
> -	return ret;
> +static int pmc_ipc_err_code(int status) {
> +	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
> +			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
> 
> -/**
> - * intel_pmc_ipc_command() -  IPC command with input/output data
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - *
> - * Send an IPC command to PMC with input/output data.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -			  u32 *out, u32 outlen)
> +static int pmc_ipc_busy_check(int status)
>  {
> -	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
> +	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
> 
> -static irqreturn_t ioc(int irq, void *dev_id)
> +static u32 pmc_ipc_enable_msi(u32 cmd)
>  {
> -	int status;
> +	return cmd | IPC_DEV_PMC_CMD_MSI;
> +}
> 
> -	if (ipcdev.irq_mode) {
> -		status = ipc_read_status();
> -		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base +
> IPC_STATUS);
> -	}
> -	complete(&ipcdev.cmd_complete);
> +static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
> +		struct device *pmc_dev,
> +		void __iomem *base,
> +		int irq)
> +{
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +	struct regmap *cmd_regs;
> +
> +	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
> +	if (!cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return ERR_PTR(-ENOMEM);
> +
> +        cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
> +			&pmc_regmap_config);
> +        if (IS_ERR(cmd_regs)) {
> +                dev_err(pmc_dev, "cmd_regs regmap init failed\n");
> +                return ERR_CAST(cmd_regs);;
> +        }
> 
> -	return IRQ_HANDLED;
> +	/* set IPC dev ops */
> +	ops->to_err_code = pmc_ipc_err_code;
> +	ops->busy_check = pmc_ipc_busy_check;
> +	ops->enable_msi = pmc_ipc_enable_msi;
> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> +	ops->pre_irq_handler_fn = pre_irq_handler_fn;
> +
> +	/* set cfg options */
> +	if (irq > 0)
> +		cfg->mode = IPC_DEV_MODE_IRQ;
> +	else
> +		cfg->mode = IPC_DEV_MODE_POLLING;
> +
> +	cfg->chan_type = IPC_CHANNEL_IA_PMC;
> +	cfg->irq = irq;
> +	cfg->use_msi = true;
> +	cfg->support_sptr = true;
> +	cfg->support_dptr = true;
> +	cfg->cmd_regs = cmd_regs;
> +	cfg->data_regs = cmd_regs;
> +	cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
> +	cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
> +	cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
> +	cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
> +	cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
> +
> +	return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV,
> cfg,
> +ops);
>  }
> 
>  static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> @@ -428,8 +333,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const
> struct pci_device_id *id)
>  	if (pmc->dev)
>  		return -EBUSY;
> 
> -	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
> -
>  	ret = pcim_enable_device(pdev);
>  	if (ret)
>  		return ret;
> @@ -438,15 +341,14 @@ static int ipc_pci_probe(struct pci_dev *pdev,
> const struct pci_device_id *id)
>  	if (ret)
>  		return ret;
> 
> -	init_completion(&pmc->cmd_complete);
> -
>  	pmc->ipc_base = pcim_iomap_table(pdev)[0];
> 
> -	ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0,
> "intel_pmc_ipc",
> -				pmc);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return ret;
> +	pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, pdev->irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev,
> +				"Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
>  	}
> 
>  	pmc->dev = &pdev->dev;
> @@ -474,19 +376,19 @@ static ssize_t
> intel_pmc_ipc_simple_cmd_store(struct device *dev,
>  					      struct device_attribute *attr,
>  					      const char *buf, size_t count)  {
> -	int subcmd;
> -	int cmd;
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
> +	int cmd[2];
>  	int ret;
> 
> -	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
> +	ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
>  	if (ret != 2) {
>  		dev_err(dev, "Error args\n");
>  		return -EINVAL;
>  	}
> 
> -	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command %d error with %d\n", cmd, ret);
> +		dev_err(dev, "command %d error with %d\n", cmd[0], ret);
>  		return ret;
>  	}
>  	return (ssize_t)count;
> @@ -496,22 +398,23 @@ static ssize_t
> intel_pmc_ipc_northpeak_store(struct device *dev,
>  					     struct device_attribute *attr,
>  					     const char *buf, size_t count)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
>  	unsigned long val;
> -	int subcmd;
> +	int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
>  	int ret;
> 
>  	if (kstrtoul(buf, 0, &val))
>  		return -EINVAL;
> 
>  	if (val)
> -		subcmd = 1;
> -	else
> -		subcmd = 0;
> -	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL,
> subcmd);
> +		cmd[1] = 1;
> +
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command north %d error with %d\n", subcmd,
> ret);
> +		dev_err(dev, "command north %d error with %d\n", cmd[1],
> ret);
>  		return ret;
>  	}
> +
>  	return (ssize_t)count;
>  }
> 
> @@ -689,6 +592,7 @@ static int ipc_create_pmc_devices(struct
> platform_device *pdev)
> 
>  static int ipc_plat_get_res(struct platform_device *pdev)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
>  	struct resource *res;
>  	void __iomem *addr;
> 
> @@ -707,9 +611,9 @@ static int ipc_plat_get_res(struct platform_device
> *pdev)
>  	if (IS_ERR(addr))
>  		return PTR_ERR(addr);
> 
> -	ipcdev.ipc_base = addr;
> -	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> -	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
> +	pmc->ipc_base = addr;
> +	pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> +	dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
> 
>  	return 0;
>  }
> @@ -755,14 +659,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
> 
>  static int ipc_plat_probe(struct platform_device *pdev)  {
> -	int ret;
> +	int ret, irq;
> +	struct intel_pmc_ipc_dev *pmc = &ipcdev;
> +
> +	pmc->dev = &pdev->dev;
> 
> -	ipcdev.dev = &pdev->dev;
> -	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
> -	init_completion(&ipcdev.cmd_complete);
> +	dev_set_drvdata(&pdev->dev, pmc);
> 
> -	ipcdev.irq = platform_get_irq(pdev, 0);
> -	if (ipcdev.irq < 0) {
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
>  		dev_err(&pdev->dev, "Failed to get irq\n");
>  		return -EINVAL;
>  	}
> @@ -773,11 +678,11 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
> -			ipcdev.gcr_mem_base, &gcr_regmap_config);
> -        if (IS_ERR(ipcdev.gcr_regs)) {
> -                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
> -                return PTR_ERR(ipcdev.gcr_regs);;
> +        pmc->gcr_regs = devm_regmap_init_mmio_clk(pmc->dev, NULL,
> +			pmc->gcr_mem_base, &gcr_regmap_config);
> +        if (IS_ERR(pmc->gcr_regs)) {
> +                dev_err(&pdev->dev, "gcr_regs regmap init failed\n");
> +                return PTR_ERR(pmc->gcr_regs);;

Extra semicolon on preceding line.

>          }
> 
>  	ret = ipc_create_pmc_devices(pdev);
> @@ -786,20 +691,20 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,
> IRQF_NO_SUSPEND,
> -			     "intel_pmc_ipc", &ipcdev)) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return -EBUSY;
> -	}
> -
>  	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
>  			ret);
> -		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
>  		return ret;
>  	}
> 
> +	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
> +	}
> +
>  	return 0;
>  }
> 

This part below can logically be a separate patch (alongwith intel_soc_pmic_bxtwc.c file too)

> diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c
> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> index bf8284a..a08a620 100644
> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> @@ -56,10 +56,6 @@
>  #define IOSS_TELEM_TRACE_CTL_WRITE	0x6
>  #define IOSS_TELEM_EVENT_CTL_READ	0x7
>  #define IOSS_TELEM_EVENT_CTL_WRITE	0x8
> -#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE	0x4
> -#define IOSS_TELEM_READ_WORD		0x1
> -#define IOSS_TELEM_WRITE_FOURBYTES	0x4
> -#define IOSS_TELEM_EVT_WRITE_SIZE	0x3
> 
>  #define TELEM_INFO_SRAMEVTS_MASK	0xFF00
>  #define TELEM_INFO_SRAMEVTS_SHIFT	0x8
> @@ -98,7 +94,7 @@ struct telem_ssram_region {  };
> 
>  static struct telemetry_plt_config *telm_conf; -static struct intel_ipc_dev
> *punit_bios_ipc_dev;
> +static struct intel_ipc_dev *punit_bios_ipc_dev, *pmc_ipc_dev;
> 
>  /*
>   * The following counters are programmed by default during setup.
> @@ -267,17 +263,16 @@ static int telemetry_check_evtid(enum
> telemetry_unit telem_unit,  static inline int
> telemetry_plt_config_ioss_event(u32 evt_id, int index)  {
>  	u32 write_buf;
> -	int ret;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	write_buf <<= BITS_PER_BYTE;
>  	write_buf |= index;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_WRITE, (u8
> *)&write_buf,
> -				    IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_WRITE);
> 
> -	return ret;
> +	return ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);

1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
2) pmc_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like telemetry_pmc_send_cmd(). Same thoughout.

>  }
> 
>  static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) @@ -
> 287,6 +282,7 @@ static inline int telemetry_plt_config_pss_event(u32
> evt_id, int index)
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> +
>  	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
>  			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);  }
> @@ -298,15 +294,16 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	int ret, index, idx;
>  	u32 *ioss_evtmap;
>  	u32 telem_ctrl;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	num_ioss_evts = evtconfig.num_evts;
>  	ioss_period = evtconfig.period;
>  	ioss_evtmap = evtconfig.evtmap;
> 
>  	/* Get telemetry EVENT CTL */
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
> -				    &telem_ctrl, IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&telem_ctrl, 1, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Read Failed\n");
>  		return ret;
> @@ -314,12 +311,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* Disable Telemetry */
>  	TELEM_DISABLE(telem_ctrl);
> -
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -				    NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
>  		return ret;
> @@ -330,12 +324,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	if (action == TELEM_RESET) {
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl),
> +				NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -360,11 +353,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -412,10 +405,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	TELEM_ENABLE_PERIODIC(telem_ctrl);
>  	telem_ctrl |= ioss_period;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL,
> 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
>  		return ret;
> @@ -609,8 +601,9 @@ static int telemetry_setup(struct platform_device
> *pdev)
>  	u32 cmd[PUNIT_PARAM_LEN] = {0};
>  	int ret;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ,
> -				    NULL, 0, &read_buf,
> IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&read_buf, 1, 0, 0);
>  	if (ret) {
>  		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
>  		return ret;
> @@ -713,9 +706,10 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		}
> 
>  		/* Get telemetry EVENT CTL */
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_READ,
> NULL, 0,
> -					    &telem_ctrl,
> IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&telem_ctrl, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Read Failed\n");
>  			goto out;
> @@ -723,12 +717,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
> 
>  		/* Disable Telemetry */
>  		TELEM_DISABLE(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			goto out;
> @@ -740,11 +733,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		TELEM_ENABLE_PERIODIC(telem_ctrl);
>  		telem_ctrl |= ioss_period;
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Enable Write
> Failed\n");
>  			goto out;
> @@ -1044,9 +1037,10 @@ static int
> telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1099,9 +1093,10 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1109,10 +1104,9 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> 
>  		TELEM_CLEAR_VERBOSITY_BITS(temp);
>  		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_WRITE, (u8
> *)&temp,
> -				IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_TRACE_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
>  			goto out;
> @@ -1157,6 +1151,10 @@ static int telemetry_pltdrv_probe(struct
> platform_device *pdev)
>  	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
>  		return PTR_ERR(punit_bios_ipc_dev);
> 
> +	pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmc_ipc_dev))
> +		return PTR_ERR(pmc_ipc_dev);
> +
>  	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> 
>  	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> 1236,6 +1234,7 @@ static int telemetry_pltdrv_probe(struct platform_device
> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>  	telemetry_clear_pltdata();
> +	intel_ipc_dev_put(pmc_ipc_dev);
>  	intel_ipc_dev_put(punit_bios_ipc_dev);
>  	iounmap(telm_conf->pss_config.regmap);
>  	iounmap(telm_conf->ioss_config.regmap);
> diff --git a/include/linux/mfd/intel_soc_pmic.h
> b/include/linux/mfd/intel_soc_pmic.h
> index 5aacdb0..7cc39b6 100644
> --- a/include/linux/mfd/intel_soc_pmic.h
> +++ b/include/linux/mfd/intel_soc_pmic.h
> @@ -20,6 +20,7 @@
>  #define __INTEL_SOC_PMIC_H__
> 
>  #include <linux/regmap.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> 
>  struct intel_soc_pmic {
>  	int irq;
> @@ -31,6 +32,7 @@ struct intel_soc_pmic {
>  	struct regmap_irq_chip_data *irq_chip_data_chgr;
>  	struct regmap_irq_chip_data *irq_chip_data_crit;
>  	struct device *dev;
> +	struct intel_ipc_dev *ipc_dev;
>  };
> 
>  #endif	/* __INTEL_SOC_PMIC_H__ */
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
@ 2017-10-09  5:18     ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-09  5:18 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, x86-DgEjT+Ai2ygdnm+yROfE0A,
	wim-IQzOog9fTRqzQB+pC5nmwQ, mingo-H+wXaHxf7aLQT0dZR+AlfA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Zha, Qipeng,
	hpa-YMNOUZJC4hwAvxtiuMwx3w, dvhart-wEGCiKHe2LqWVfeAwA7xHQ,
	tglx-hfZtesqFncYOwBW4kG4KsQ, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	andy-wEGCiKHe2LqWVfeAwA7xHQ
  Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	platform-driver-x86-u79uwXL29TY76Z2rM5mHXA,
	sathyaosid-Re5JQEeQqe8AvxtiuMwx3w

> From: sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
> [mailto:sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org]
> Sent: Sunday, October 8, 2017 3:50 AM
> To: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org; x86-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org;
> mingo-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org; alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org; Zha, Qipeng
> <qipeng.zha-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>; hpa-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org; dvhart-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org;
> tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org; lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org; andy-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org; Chakravarty,
> Souvik K <souvik.k.chakravarty-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Cc: linux-rtc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-watchdog-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
> kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; platform-driver-x86-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> sathyaosid-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org; Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> Subject: [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC
> device calls
> 
> From: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> 
> Removed redundant IPC helper functions and refactored the driver to use
> generic IPC device driver APIs. Also, cleaned up the driver to minimize the
> usage of global variable ipcdev by propogating the struct intel_pmc_ipc_dev
> pointer or by getting it from device private data.
> 
> This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
> intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan
> <sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> ---
>  arch/x86/include/asm/intel_pmc_ipc.h          |  37 +--
>  drivers/mfd/intel_soc_pmic_bxtwc.c            |  21 +-
>  drivers/platform/x86/intel_pmc_ipc.c          | 393 ++++++++++----------------
>  drivers/platform/x86/intel_telemetry_pltdrv.c | 119 ++++----
>  include/linux/mfd/intel_soc_pmic.h            |   2 +
>  5 files changed, 238 insertions(+), 334 deletions(-)
> 
> Changes since v4:
>  * None
> 
> Changes since v3:
>  * Added unique name to PMC regmaps.
>  * Added support to clear interrupt bit.
>  * Added intel_ipc_dev_put() support.
> 
> Changes since v1:
>  * Removed custom APIs.
>  * Cleaned up PMC IPC user drivers to use APIs provided by generic
>    IPC driver.
> 
> diff --git a/arch/x86/include/asm/intel_pmc_ipc.h
> b/arch/x86/include/asm/intel_pmc_ipc.h
> index fac89eb..9fc7c3c 100644
> --- a/arch/x86/include/asm/intel_pmc_ipc.h
> +++ b/arch/x86/include/asm/intel_pmc_ipc.h
> @@ -1,10 +1,15 @@
>  #ifndef _ASM_X86_INTEL_PMC_IPC_H_
>  #define  _ASM_X86_INTEL_PMC_IPC_H_
> 
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> +
> +#define INTEL_PMC_IPC_DEV		"intel_pmc_ipc"
> +#define PMC_PARAM_LEN			2
> +
>  /* Commands */
>  #define PMC_IPC_PMIC_ACCESS		0xFF
> -#define		PMC_IPC_PMIC_ACCESS_READ	0x0
> -#define		PMC_IPC_PMIC_ACCESS_WRITE	0x1
> +#define	PMC_IPC_PMIC_ACCESS_READ	0x0
> +#define	PMC_IPC_PMIC_ACCESS_WRITE	0x1
>  #define PMC_IPC_USB_PWR_CTRL		0xF0
>  #define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
>  #define PMC_IPC_PHY_CONFIG		0xEE
> @@ -28,13 +33,14 @@
>  #define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
>  #define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> 
> +static inline void pmc_cmd_init(u32 *cmd, u32 param1, u32 param2) {
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +}
> +
>  #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
> 
> -int intel_pmc_ipc_simple_command(int cmd, int sub); -int
> intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr);
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -		u32 *out, u32 outlen);
>  int intel_pmc_s0ix_counter_read(u64 *data);  int intel_pmc_gcr_read(u32
> offset, u32 *data);  int intel_pmc_gcr_write(u32 offset, u32 data); @@ -42,23
> +48,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
> 
>  #else
> 
> -static inline int intel_pmc_ipc_simple_command(int cmd, int sub) -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen, u32 dptr, u32 sptr)
> -{
> -	return -EINVAL;
> -}
> -
> -static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32
> inlen,
> -		u32 *out, u32 outlen)
> -{
> -	return -EINVAL;
> -}
> -
>  static inline int intel_pmc_s0ix_counter_read(u64 *data)  {
>  	return -EINVAL;
> diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c
> b/drivers/mfd/intel_soc_pmic_bxtwc.c
> index 15bc052..f9df9e7 100644
> --- a/drivers/mfd/intel_soc_pmic_bxtwc.c
> +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
> @@ -271,6 +271,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
>  	u8 ipc_in[2];
>  	u8 ipc_out[4];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_READ};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -284,9 +286,8 @@ static int regmap_ipc_byte_reg_read(void *context,
> unsigned int reg,
> 
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_READ,
> -			ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), (u32 *)ipc_out, 1, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to read from PMIC\n");
>  		return ret;
> @@ -303,6 +304,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	int i2c_addr;
>  	u8 ipc_in[3];
>  	struct intel_soc_pmic *pmic = context;
> +	u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
> +		PMC_IPC_PMIC_ACCESS_WRITE};
> 
>  	if (!pmic)
>  		return -EINVAL;
> @@ -317,9 +320,8 @@ static int regmap_ipc_byte_reg_write(void *context,
> unsigned int reg,
>  	ipc_in[0] = reg;
>  	ipc_in[1] = i2c_addr;
>  	ipc_in[2] = val;
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
> -			PMC_IPC_PMIC_ACCESS_WRITE,
> -			ipc_in, sizeof(ipc_in), NULL, 0);
> +	ret = ipc_dev_raw_cmd(pmic->ipc_dev, cmd, PMC_PARAM_LEN,
> ipc_in,
> +			sizeof(ipc_in), NULL, 0, 0, 0);
>  	if (ret) {
>  		dev_err(pmic->dev, "Failed to write to PMIC\n");
>  		return ret;
> @@ -445,6 +447,10 @@ static int bxtwc_probe(struct platform_device
> *pdev)
>  	if (!pmic)
>  		return -ENOMEM;
> 
> +	pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmic->ipc_dev))
> +		return PTR_ERR(pmic->ipc_dev);
> +
>  	ret = platform_get_irq(pdev, 0);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "Invalid IRQ\n"); @@ -562,7 +568,10
> @@ static int bxtwc_probe(struct platform_device *pdev)
> 
>  static int bxtwc_remove(struct platform_device *pdev)  {
> +	struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
> +
>  	sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
> +	intel_ipc_dev_put(pmic->ipc_dev);
> 
>  	return 0;
>  }
> diff --git a/drivers/platform/x86/intel_pmc_ipc.c
> b/drivers/platform/x86/intel_pmc_ipc.c
> index fd5bcdf..35d4734 100644
> --- a/drivers/platform/x86/intel_pmc_ipc.c
> +++ b/drivers/platform/x86/intel_pmc_ipc.c
> @@ -47,18 +47,8 @@
>   * The ARC handles the interrupt and services it, writing optional data to
>   * the IPC1 registers, updates the IPC_STS response register with the status.
>   */
> -#define IPC_CMD			0x0
> -#define		IPC_CMD_MSI		0x100
>  #define		IPC_CMD_SIZE		16
>  #define		IPC_CMD_SUBCMD		12
> -#define IPC_STATUS		0x04
> -#define		IPC_STATUS_IRQ		0x4
> -#define		IPC_STATUS_ERR		0x2
> -#define		IPC_STATUS_BUSY		0x1
> -#define IPC_SPTR		0x08
> -#define IPC_DPTR		0x0C
> -#define IPC_WRITE_BUFFER	0x80
> -#define IPC_READ_BUFFER		0x90
> 
>  /* Residency with clock rate at 19.2MHz to usecs */
>  #define S0IX_RESIDENCY_IN_USECS(d, s)		\
> @@ -73,11 +63,6 @@
>   */
>  #define IPC_DATA_BUFFER_SIZE	16
> 
> -#define IPC_LOOP_CNT		3000000
> -#define IPC_MAX_SEC		3
> -
> -#define IPC_TRIGGER_MODE_IRQ		true
> -
>  /* exported resources from IFWI */
>  #define PLAT_RESOURCE_IPC_INDEX		0
>  #define PLAT_RESOURCE_IPC_SIZE		0x1000
> @@ -117,36 +102,41 @@
>  #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
>  #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
> 
> +/* IPC PMC commands */
> +#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
> +#define	IPC_DEV_PMC_CMD_SIZE			16
> +#define	IPC_DEV_PMC_CMD_SUBCMD			12
> +#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
> +#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK
> 	GENMASK(7, 0)
> +#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
> +
> +/*IPC PMC reg offsets */
> +#define IPC_DEV_PMC_STATUS_OFFSET		0x04
> +#define IPC_DEV_PMC_SPTR_OFFSET			0x08
> +#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
> +#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
> +#define IPC_DEV_PMC_RBUF_OFFSET			0x90
> +
>  static struct intel_pmc_ipc_dev {
>  	struct device *dev;
> +	struct intel_ipc_dev *pmc_ipc_dev;
> +	struct intel_ipc_dev_ops ops;
> +	struct intel_ipc_dev_cfg cfg;
>  	void __iomem *ipc_base;
> -	bool irq_mode;
> -	int irq;
> -	int cmd;
> -	struct completion cmd_complete;
> 
>  	/* gcr */
>  	void __iomem *gcr_mem_base;
>  	struct regmap *gcr_regs;
>  } ipcdev;
> 
> -static char *ipc_err_sources[] = {
> -	[IPC_ERR_NONE] =
> -		"no error",
> -	[IPC_ERR_CMD_NOT_SUPPORTED] =
> -		"command not supported",
> -	[IPC_ERR_CMD_NOT_SERVICED] =
> -		"command not serviced",
> -	[IPC_ERR_UNABLE_TO_SERVICE] =
> -		"unable to service",
> -	[IPC_ERR_CMD_INVALID] =
> -		"command invalid",
> -	[IPC_ERR_CMD_FAILED] =
> -		"command failed",
> -	[IPC_ERR_EMSECURITY] =
> -		"Invalid Battery",
> -	[IPC_ERR_UNSIGNEDKERNEL] =
> -		"Unsigned kernel",
> +static struct regmap_config pmc_regmap_config = {
> +	.name = "intel_pmc_regs",
> +        .reg_bits = 32,
> +        .reg_stride = 4,
> +        .val_bits = 32,
> +	.fast_io = true,
>  };
> 
>  static struct regmap_config gcr_regmap_config = { @@ -158,40 +148,6 @@
> static struct regmap_config gcr_regmap_config = {
>  	.max_register = PLAT_RESOURCE_GCR_SIZE,  };
> 
> -/* Prevent concurrent calls to the PMC */ -static DEFINE_MUTEX(ipclock);
> -
> -static inline void ipc_send_command(u32 cmd) -{
> -	ipcdev.cmd = cmd;
> -	if (ipcdev.irq_mode) {
> -		reinit_completion(&ipcdev.cmd_complete);
> -		cmd |= IPC_CMD_MSI;
> -	}
> -	writel(cmd, ipcdev.ipc_base + IPC_CMD);
> -}
> -
> -static inline u32 ipc_read_status(void) -{
> -	return readl(ipcdev.ipc_base + IPC_STATUS);
> -}
> -
> -static inline void ipc_data_writel(u32 data, u32 offset) -{
> -	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
> -}
> -
> -static inline u8 __maybe_unused ipc_data_readb(u32 offset) -{
> -	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -static inline u32 ipc_data_readl(u32 offset) -{
> -	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
> -}
> -
> -
>  /**
>   * intel_pmc_gcr_read() - Read PMC GCR register
>   * @offset:	offset of GCR register from GCR address base
> @@ -263,160 +219,109 @@ static int update_no_reboot_bit(void *priv, bool
> set)
>  				    PMC_CFG_NO_REBOOT_MASK, value);  }
> 
> -static int intel_pmc_ipc_check_status(void)
> +static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>  {
> -	int status;
> -	int ret = 0;
> -
> -	if (ipcdev.irq_mode) {
> -		if (0 == wait_for_completion_timeout(
> -				&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
> -			ret = -ETIMEDOUT;
> -	} else {
> -		int loop_count = IPC_LOOP_CNT;
> -
> -		while ((ipc_read_status() & IPC_STATUS_BUSY) && --
> loop_count)
> -			udelay(1);
> -		if (loop_count == 0)
> -			ret = -ETIMEDOUT;
> -	}
> -
> -	status = ipc_read_status();
> -	if (ret == -ETIMEDOUT) {
> -		dev_err(ipcdev.dev,
> -			"IPC timed out, TS=0x%x, CMD=0x%x\n",
> -			status, ipcdev.cmd);
> -		return ret;
> -	}
> +	if (!cmd_list || cmdlen != PMC_PARAM_LEN)
> +		return -EINVAL;
> 
> -	if (status & IPC_STATUS_ERR) {
> -		int i;
> -
> -		ret = -EIO;
> -		i = (status >> IPC_CMD_SIZE) & 0xFF;
> -		if (i < ARRAY_SIZE(ipc_err_sources))
> -			dev_err(ipcdev.dev,
> -				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> -				ipc_err_sources[i], status, ipcdev.cmd);
> -		else
> -			dev_err(ipcdev.dev,
> -				"IPC failed: unknown, STS=0x%x,
> CMD=0x%x\n",
> -				status, ipcdev.cmd);
> -		if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i ==
> IPC_ERR_EMSECURITY))
> -			ret = -EACCES;
> -	}
> +	cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
> 
> -	return ret;
> +	return 0;
>  }
> 
> -/**
> - * intel_pmc_ipc_simple_command() - Simple IPC command
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - *
> - * Send a simple IPC command to PMC when don't need to specify
> - * input/output data and source/dest pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_simple_command(int cmd, int sub)
> +static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +		u32 *out, u32 outlen, u32 dptr, u32 sptr)
>  {
>  	int ret;
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	mutex_unlock(&ipclock);
> +	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE/4)
> +		return -EINVAL;
> 
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
> +	if (ret < 0)
> +		return ret;
> 
> -/**
> - * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - * @sptr:	data writing to SPTR register.
> - * @dptr:	data writing to DPTR register.
> - *
> - * Send an IPC command to PMC with input/output data and source/dest
> pointers.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
> -			  u32 outlen, u32 dptr, u32 sptr)
> -{
> -	u32 wbuf[4] = { 0 };
> -	int ret;
> -	int i;
> +	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
> 
> -	if (inlen > IPC_DATA_BUFFER_SIZE || outlen >
> IPC_DATA_BUFFER_SIZE / 4)
> -		return -EINVAL;
> +	return 0;
> +}
> 
> -	mutex_lock(&ipclock);
> -	if (ipcdev.dev == NULL) {
> -		mutex_unlock(&ipclock);
> -		return -ENODEV;
> -	}
> -	memcpy(wbuf, in, inlen);
> -	writel(dptr, ipcdev.ipc_base + IPC_DPTR);
> -	writel(sptr, ipcdev.ipc_base + IPC_SPTR);
> -	/* The input data register is 32bit register and inlen is in Byte */
> -	for (i = 0; i < ((inlen + 3) / 4); i++)
> -		ipc_data_writel(wbuf[i], 4 * i);
> -	ipc_send_command((inlen << IPC_CMD_SIZE) |
> -			(sub << IPC_CMD_SUBCMD) | cmd);
> -	ret = intel_pmc_ipc_check_status();
> -	if (!ret) {
> -		/* out is read from 32bit register and outlen is in 32bit */
> -		for (i = 0; i < outlen; i++)
> -			*out++ = ipc_data_readl(4 * i);
> -	}
> -	mutex_unlock(&ipclock);
> +static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq) {
> +	return regmap_write_bits(ipc_dev->cfg->cmd_regs,
> +				  ipc_dev->cfg->status_reg,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ,
> +				  IPC_DEV_PMC_CMD_STATUS_IRQ);
> +}
> 
> -	return ret;
> +static int pmc_ipc_err_code(int status) {
> +	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
> +			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
> 
> -/**
> - * intel_pmc_ipc_command() -  IPC command with input/output data
> - * @cmd:	IPC command code.
> - * @sub:	IPC command sub type.
> - * @in:		input data of this IPC command.
> - * @inlen:	input data length in bytes.
> - * @out:	output data of this IPC command.
> - * @outlen:	output data length in dwords.
> - *
> - * Send an IPC command to PMC with input/output data.
> - *
> - * Return:	an IPC error code or 0 on success.
> - */
> -int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
> -			  u32 *out, u32 outlen)
> +static int pmc_ipc_busy_check(int status)
>  {
> -	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
> +	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
>  }
> -EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
> 
> -static irqreturn_t ioc(int irq, void *dev_id)
> +static u32 pmc_ipc_enable_msi(u32 cmd)
>  {
> -	int status;
> +	return cmd | IPC_DEV_PMC_CMD_MSI;
> +}
> 
> -	if (ipcdev.irq_mode) {
> -		status = ipc_read_status();
> -		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base +
> IPC_STATUS);
> -	}
> -	complete(&ipcdev.cmd_complete);
> +static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
> +		struct device *pmc_dev,
> +		void __iomem *base,
> +		int irq)
> +{
> +	struct intel_ipc_dev_ops *ops;
> +	struct intel_ipc_dev_cfg *cfg;
> +	struct regmap *cmd_regs;
> +
> +	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
> +	if (!cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
> +	if (!ops)
> +		return ERR_PTR(-ENOMEM);
> +
> +        cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
> +			&pmc_regmap_config);
> +        if (IS_ERR(cmd_regs)) {
> +                dev_err(pmc_dev, "cmd_regs regmap init failed\n");
> +                return ERR_CAST(cmd_regs);;
> +        }
> 
> -	return IRQ_HANDLED;
> +	/* set IPC dev ops */
> +	ops->to_err_code = pmc_ipc_err_code;
> +	ops->busy_check = pmc_ipc_busy_check;
> +	ops->enable_msi = pmc_ipc_enable_msi;
> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> +	ops->pre_irq_handler_fn = pre_irq_handler_fn;
> +
> +	/* set cfg options */
> +	if (irq > 0)
> +		cfg->mode = IPC_DEV_MODE_IRQ;
> +	else
> +		cfg->mode = IPC_DEV_MODE_POLLING;
> +
> +	cfg->chan_type = IPC_CHANNEL_IA_PMC;
> +	cfg->irq = irq;
> +	cfg->use_msi = true;
> +	cfg->support_sptr = true;
> +	cfg->support_dptr = true;
> +	cfg->cmd_regs = cmd_regs;
> +	cfg->data_regs = cmd_regs;
> +	cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
> +	cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
> +	cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
> +	cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
> +	cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
> +
> +	return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV,
> cfg,
> +ops);
>  }
> 
>  static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> @@ -428,8 +333,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const
> struct pci_device_id *id)
>  	if (pmc->dev)
>  		return -EBUSY;
> 
> -	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
> -
>  	ret = pcim_enable_device(pdev);
>  	if (ret)
>  		return ret;
> @@ -438,15 +341,14 @@ static int ipc_pci_probe(struct pci_dev *pdev,
> const struct pci_device_id *id)
>  	if (ret)
>  		return ret;
> 
> -	init_completion(&pmc->cmd_complete);
> -
>  	pmc->ipc_base = pcim_iomap_table(pdev)[0];
> 
> -	ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0,
> "intel_pmc_ipc",
> -				pmc);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return ret;
> +	pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, pdev->irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev,
> +				"Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
>  	}
> 
>  	pmc->dev = &pdev->dev;
> @@ -474,19 +376,19 @@ static ssize_t
> intel_pmc_ipc_simple_cmd_store(struct device *dev,
>  					      struct device_attribute *attr,
>  					      const char *buf, size_t count)  {
> -	int subcmd;
> -	int cmd;
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
> +	int cmd[2];
>  	int ret;
> 
> -	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
> +	ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
>  	if (ret != 2) {
>  		dev_err(dev, "Error args\n");
>  		return -EINVAL;
>  	}
> 
> -	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command %d error with %d\n", cmd, ret);
> +		dev_err(dev, "command %d error with %d\n", cmd[0], ret);
>  		return ret;
>  	}
>  	return (ssize_t)count;
> @@ -496,22 +398,23 @@ static ssize_t
> intel_pmc_ipc_northpeak_store(struct device *dev,
>  					     struct device_attribute *attr,
>  					     const char *buf, size_t count)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
>  	unsigned long val;
> -	int subcmd;
> +	int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
>  	int ret;
> 
>  	if (kstrtoul(buf, 0, &val))
>  		return -EINVAL;
> 
>  	if (val)
> -		subcmd = 1;
> -	else
> -		subcmd = 0;
> -	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL,
> subcmd);
> +		cmd[1] = 1;
> +
> +	ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
>  	if (ret) {
> -		dev_err(dev, "command north %d error with %d\n", subcmd,
> ret);
> +		dev_err(dev, "command north %d error with %d\n", cmd[1],
> ret);
>  		return ret;
>  	}
> +
>  	return (ssize_t)count;
>  }
> 
> @@ -689,6 +592,7 @@ static int ipc_create_pmc_devices(struct
> platform_device *pdev)
> 
>  static int ipc_plat_get_res(struct platform_device *pdev)  {
> +	struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
>  	struct resource *res;
>  	void __iomem *addr;
> 
> @@ -707,9 +611,9 @@ static int ipc_plat_get_res(struct platform_device
> *pdev)
>  	if (IS_ERR(addr))
>  		return PTR_ERR(addr);
> 
> -	ipcdev.ipc_base = addr;
> -	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> -	dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
> +	pmc->ipc_base = addr;
> +	pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> +	dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
> 
>  	return 0;
>  }
> @@ -755,14 +659,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
> 
>  static int ipc_plat_probe(struct platform_device *pdev)  {
> -	int ret;
> +	int ret, irq;
> +	struct intel_pmc_ipc_dev *pmc = &ipcdev;
> +
> +	pmc->dev = &pdev->dev;
> 
> -	ipcdev.dev = &pdev->dev;
> -	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
> -	init_completion(&ipcdev.cmd_complete);
> +	dev_set_drvdata(&pdev->dev, pmc);
> 
> -	ipcdev.irq = platform_get_irq(pdev, 0);
> -	if (ipcdev.irq < 0) {
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
>  		dev_err(&pdev->dev, "Failed to get irq\n");
>  		return -EINVAL;
>  	}
> @@ -773,11 +678,11 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -        ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
> -			ipcdev.gcr_mem_base, &gcr_regmap_config);
> -        if (IS_ERR(ipcdev.gcr_regs)) {
> -                dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
> -                return PTR_ERR(ipcdev.gcr_regs);;
> +        pmc->gcr_regs = devm_regmap_init_mmio_clk(pmc->dev, NULL,
> +			pmc->gcr_mem_base, &gcr_regmap_config);
> +        if (IS_ERR(pmc->gcr_regs)) {
> +                dev_err(&pdev->dev, "gcr_regs regmap init failed\n");
> +                return PTR_ERR(pmc->gcr_regs);;

Extra semicolon on preceding line.

>          }
> 
>  	ret = ipc_create_pmc_devices(pdev);
> @@ -786,20 +691,20 @@ static int ipc_plat_probe(struct platform_device
> *pdev)
>  		return ret;
>  	}
> 
> -	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,
> IRQF_NO_SUSPEND,
> -			     "intel_pmc_ipc", &ipcdev)) {
> -		dev_err(&pdev->dev, "Failed to request irq\n");
> -		return -EBUSY;
> -	}
> -
>  	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
>  	if (ret) {
>  		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
>  			ret);
> -		devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
>  		return ret;
>  	}
> 
> +	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
> +			pmc->ipc_base, irq);
> +	if (IS_ERR(pmc->pmc_ipc_dev)) {
> +		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
> +		return PTR_ERR(pmc->pmc_ipc_dev);
> +	}
> +
>  	return 0;
>  }
> 

This part below can logically be a separate patch (alongwith intel_soc_pmic_bxtwc.c file too)

> diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c
> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> index bf8284a..a08a620 100644
> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> @@ -56,10 +56,6 @@
>  #define IOSS_TELEM_TRACE_CTL_WRITE	0x6
>  #define IOSS_TELEM_EVENT_CTL_READ	0x7
>  #define IOSS_TELEM_EVENT_CTL_WRITE	0x8
> -#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE	0x4
> -#define IOSS_TELEM_READ_WORD		0x1
> -#define IOSS_TELEM_WRITE_FOURBYTES	0x4
> -#define IOSS_TELEM_EVT_WRITE_SIZE	0x3
> 
>  #define TELEM_INFO_SRAMEVTS_MASK	0xFF00
>  #define TELEM_INFO_SRAMEVTS_SHIFT	0x8
> @@ -98,7 +94,7 @@ struct telem_ssram_region {  };
> 
>  static struct telemetry_plt_config *telm_conf; -static struct intel_ipc_dev
> *punit_bios_ipc_dev;
> +static struct intel_ipc_dev *punit_bios_ipc_dev, *pmc_ipc_dev;
> 
>  /*
>   * The following counters are programmed by default during setup.
> @@ -267,17 +263,16 @@ static int telemetry_check_evtid(enum
> telemetry_unit telem_unit,  static inline int
> telemetry_plt_config_ioss_event(u32 evt_id, int index)  {
>  	u32 write_buf;
> -	int ret;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	write_buf <<= BITS_PER_BYTE;
>  	write_buf |= index;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_WRITE, (u8
> *)&write_buf,
> -				    IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_WRITE);
> 
> -	return ret;
> +	return ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);

1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
2) pmc_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like telemetry_pmc_send_cmd(). Same thoughout.

>  }
> 
>  static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) @@ -
> 287,6 +282,7 @@ static inline int telemetry_plt_config_pss_event(u32
> evt_id, int index)
> 
>  	write_buf = evt_id | TELEM_EVENT_ENABLE;
>  	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> +
>  	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> PUNIT_PARAM_LEN,
>  			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);  }
> @@ -298,15 +294,16 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	int ret, index, idx;
>  	u32 *ioss_evtmap;
>  	u32 telem_ctrl;
> +	u32 cmd[PMC_PARAM_LEN] = {0};
> 
>  	num_ioss_evts = evtconfig.num_evts;
>  	ioss_period = evtconfig.period;
>  	ioss_evtmap = evtconfig.evtmap;
> 
>  	/* Get telemetry EVENT CTL */
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
> -				    &telem_ctrl, IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&telem_ctrl, 1, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Read Failed\n");
>  		return ret;
> @@ -314,12 +311,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
> 
>  	/* Disable Telemetry */
>  	TELEM_DISABLE(telem_ctrl);
> -
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -				    NULL, 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
>  		return ret;
> @@ -330,12 +324,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	if (action == TELEM_RESET) {
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl),
> +				NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -360,11 +353,11 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  		/* Clear All Events */
>  		TELEM_CLEAR_EVENTS(telem_ctrl);
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			return ret;
> @@ -412,10 +405,9 @@ static int telemetry_setup_iossevtconfig(struct
> telemetry_evtconfig evtconfig,
>  	TELEM_ENABLE_PERIODIC(telem_ctrl);
>  	telem_ctrl |= ioss_period;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				    IOSS_TELEM_EVENT_CTL_WRITE,
> -				    (u8 *)&telem_ctrl,
> -				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL,
> 0);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_EVENT_CTL_WRITE);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN,
> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>  	if (ret) {
>  		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
>  		return ret;
> @@ -609,8 +601,9 @@ static int telemetry_setup(struct platform_device
> *pdev)
>  	u32 cmd[PUNIT_PARAM_LEN] = {0};
>  	int ret;
> 
> -	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ,
> -				    NULL, 0, &read_buf,
> IOSS_TELEM_READ_WORD);
> +	pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_INFO_READ);
> +	ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd, PMC_PARAM_LEN, NULL,
> 0,
> +			&read_buf, 1, 0, 0);
>  	if (ret) {
>  		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
>  		return ret;
> @@ -713,9 +706,10 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		}
> 
>  		/* Get telemetry EVENT CTL */
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_READ,
> NULL, 0,
> -					    &telem_ctrl,
> IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&telem_ctrl, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Read Failed\n");
>  			goto out;
> @@ -723,12 +717,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
> 
>  		/* Disable Telemetry */
>  		TELEM_DISABLE(telem_ctrl);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Disable Write
> Failed\n");
>  			goto out;
> @@ -740,11 +733,11 @@ static int telemetry_plt_set_sampling_period(u8
> pss_period, u8 ioss_period)
>  		TELEM_ENABLE_PERIODIC(telem_ctrl);
>  		telem_ctrl |= ioss_period;
> 
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -					    IOSS_TELEM_EVENT_CTL_WRITE,
> -					    (u8 *)&telem_ctrl,
> -
> IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
> -					    NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_EVENT_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> +				0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TELEM_CTRL Event Enable Write
> Failed\n");
>  			goto out;
> @@ -1044,9 +1037,10 @@ static int
> telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1099,9 +1093,10 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
>  		break;
> 
>  	case TELEM_IOSS:
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_READ, NULL, 0,
> &temp,
> -				IOSS_TELEM_READ_WORD);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> +				IOSS_TELEM_TRACE_CTL_READ);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN, NULL, 0,
> +				&temp, 1, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Read Failed\n");
>  			goto out;
> @@ -1109,10 +1104,9 @@ static int
> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> 
>  		TELEM_CLEAR_VERBOSITY_BITS(temp);
>  		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> -
> -		ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> -				IOSS_TELEM_TRACE_CTL_WRITE, (u8
> *)&temp,
> -				IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
> +		pmc_cmd_init(cmd, PMC_IPC_PMC_TELEMTRY,
> IOSS_TELEM_TRACE_CTL_WRITE);
> +		ret = ipc_dev_raw_cmd(pmc_ipc_dev, cmd,
> PMC_PARAM_LEN,
> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>  		if (ret) {
>  			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
>  			goto out;
> @@ -1157,6 +1151,10 @@ static int telemetry_pltdrv_probe(struct
> platform_device *pdev)
>  	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
>  		return PTR_ERR(punit_bios_ipc_dev);
> 
> +	pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
> +	if (IS_ERR_OR_NULL(pmc_ipc_dev))
> +		return PTR_ERR(pmc_ipc_dev);
> +
>  	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> 
>  	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> 1236,6 +1234,7 @@ static int telemetry_pltdrv_probe(struct platform_device
> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>  	telemetry_clear_pltdata();
> +	intel_ipc_dev_put(pmc_ipc_dev);
>  	intel_ipc_dev_put(punit_bios_ipc_dev);
>  	iounmap(telm_conf->pss_config.regmap);
>  	iounmap(telm_conf->ioss_config.regmap);
> diff --git a/include/linux/mfd/intel_soc_pmic.h
> b/include/linux/mfd/intel_soc_pmic.h
> index 5aacdb0..7cc39b6 100644
> --- a/include/linux/mfd/intel_soc_pmic.h
> +++ b/include/linux/mfd/intel_soc_pmic.h
> @@ -20,6 +20,7 @@
>  #define __INTEL_SOC_PMIC_H__
> 
>  #include <linux/regmap.h>
> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> 
>  struct intel_soc_pmic {
>  	int irq;
> @@ -31,6 +32,7 @@ struct intel_soc_pmic {
>  	struct regmap_irq_chip_data *irq_chip_data_chgr;
>  	struct regmap_irq_chip_data *irq_chip_data_crit;
>  	struct device *dev;
> +	struct intel_ipc_dev *ipc_dev;
>  };
> 
>  #endif	/* __INTEL_SOC_PMIC_H__ */
> --
> 2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-09  4:53     ` Chakravarty, Souvik K
  (?)
@ 2017-10-09  7:11       ` Christoph Hellwig
  -1 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-09  7:11 UTC (permalink / raw)
  To: Chakravarty, Souvik K
  Cc: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy, linux-rtc, linux-watchdog, linux-kernel,
	platform-driver-x86, sathyaosid

What does IPC stand for in this device?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-09  7:11       ` Christoph Hellwig
  0 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-09  7:11 UTC (permalink / raw)
  To: Chakravarty, Souvik K
  Cc: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy, linux-rtc, linux-watchdog, linux-kernel,
	platform-driver-x86, sathyaosid

What does IPC stand for in this device?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-09  7:11       ` Christoph Hellwig
  0 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-09  7:11 UTC (permalink / raw)
  To: Chakravarty, Souvik K
  Cc: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy, linux-rtc, linux-watchdog, linux-kernel, pla

What does IPC stand for in this device?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-09  7:11       ` Christoph Hellwig
  (?)
@ 2017-10-10  0:27         ` sathyanarayanan kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10  0:27 UTC (permalink / raw)
  To: Christoph Hellwig, Chakravarty, Souvik K
  Cc: a.zummo, x86, wim, mingo, alexandre.belloni, Zha, Qipeng, hpa,
	dvhart, tglx, lee.jones, andy, linux-rtc, linux-watchdog,
	linux-kernel, platform-driver-x86, sathyaosid

Hi,


On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> What does IPC stand for in this device?
Inter processor communication

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-10  0:27         ` sathyanarayanan kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10  0:27 UTC (permalink / raw)
  To: Christoph Hellwig, Chakravarty, Souvik K
  Cc: a.zummo, x86, wim, mingo, alexandre.belloni, Zha, Qipeng, hpa,
	dvhart, tglx, lee.jones, andy, linux-rtc, linux-watchdog,
	linux-kernel, platform-driver-x86, sathyaosid

Hi,


On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> What does IPC stand for in this device?
Inter processor communication

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-10  0:27         ` sathyanarayanan kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10  0:27 UTC (permalink / raw)
  To: Christoph Hellwig, Chakravarty, Souvik K
  Cc: a.zummo, x86, wim, mingo, alexandre.belloni, Zha, Qipeng, hpa,
	dvhart, tglx, lee.jones, andy, linux-rtc, linux-watchdog,
	linux-kernel, platform-driver-x86, sathyaosid

Hi,


On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> What does IPC stand for in this device?
Inter processor communication

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-10  0:27         ` sathyanarayanan kuppuswamy
  (?)
@ 2017-10-10  6:27           ` Christoph Hellwig
  -1 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-10  6:27 UTC (permalink / raw)
  To: sathyanarayanan kuppuswamy
  Cc: Christoph Hellwig, Chakravarty, Souvik K, a.zummo, x86, wim,
	mingo, alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx,
	lee.jones, andy, linux-rtc, linux-watchdog, linux-kernel,
	platform-driver-x86, sathyaosid

On Mon, Oct 09, 2017 at 05:27:21PM -0700, sathyanarayanan kuppuswamy wrote:
> Hi,
> 
> 
> On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> > What does IPC stand for in this device?
> Inter processor communication

Id would suggest to explain that, as it's not how we usually use the
term in Linux?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-10  6:27           ` Christoph Hellwig
  0 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-10  6:27 UTC (permalink / raw)
  To: sathyanarayanan kuppuswamy
  Cc: Christoph Hellwig, Chakravarty, Souvik K, a.zummo, x86, wim,
	mingo, alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx,
	lee.jones, andy, linux-rtc, linux-watchdog, linux-kernel,
	platform-driver-x86, sathyaosid

On Mon, Oct 09, 2017 at 05:27:21PM -0700, sathyanarayanan kuppuswamy wrote:
> Hi,
> 
> 
> On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> > What does IPC stand for in this device?
> Inter processor communication

Id would suggest to explain that, as it's not how we usually use the
term in Linux?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-10  6:27           ` Christoph Hellwig
  0 siblings, 0 replies; 53+ messages in thread
From: Christoph Hellwig @ 2017-10-10  6:27 UTC (permalink / raw)
  To: sathyanarayanan kuppuswamy
  Cc: Christoph Hellwig, Chakravarty, Souvik K,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, x86-DgEjT+Ai2ygdnm+yROfE0A,
	wim-IQzOog9fTRqzQB+pC5nmwQ, mingo-H+wXaHxf7aLQT0dZR+AlfA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, Zha, Qipeng,
	hpa-YMNOUZJC4hwAvxtiuMwx3w, dvhart-wEGCiKHe2LqWVfeAwA7xHQ,
	tglx-hfZtesqFncYOwBW4kG4KsQ, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
	andy-wEGCiKHe2LqWVfeAwA7xHQ, linux-rtc-u79uwXL29TY76Z2rM5mHXA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Mon, Oct 09, 2017 at 05:27:21PM -0700, sathyanarayanan kuppuswamy wrote:
> Hi,
> 
> 
> On 10/09/2017 12:11 AM, Christoph Hellwig wrote:
> > What does IPC stand for in this device?
> Inter processor communication

Id would suggest to explain that, as it's not how we usually use the
term in Linux?
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-09  4:53     ` Chakravarty, Souvik K
@ 2017-10-10 22:09       ` sathyanarayanan kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10 22:09 UTC (permalink / raw)
  To: Chakravarty, Souvik K, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

Hi,


On 10/08/2017 09:53 PM, Chakravarty, Souvik K wrote:
>> From: sathyanarayanan.kuppuswamy@linux.intel.com
>> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
>> Sent: Sunday, October 8, 2017 3:50 AM
>> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
>> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
>> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
>> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
>> Souvik K <souvik.k.chakravarty@intel.com>
>> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
>> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
>> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
>>
>> From: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c redundantly
>> implements the same IPC features and has lot of code duplication between
>> them. This driver addresses this issue by grouping the common IPC
>> functionalities under the same driver.
>>
>> Signed-off-by: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> ---
>>   drivers/platform/x86/Kconfig                    |   8 +
>>   drivers/platform/x86/Makefile                   |   1 +
>>   drivers/platform/x86/intel_ipc_dev.c            | 576
>> ++++++++++++++++++++++++
>>   include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
>>   4 files changed, 791 insertions(+)
>>   create mode 100644 drivers/platform/x86/intel_ipc_dev.c
>>   create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
>>
>> Changes since v4:
>>   * None
>>
>> Changes since v3:
>>   * Fixed NULL pointer exception in intel_ipc_dev_get().
>>   * Fixed error in check for duplicate intel_ipc_dev.
>>   * Added custom interrupt handler support.
>>   * Used char array for error string conversion.
>>   * Added put dev support.
>>   * Added devm_* variant of intel_ipc_dev_get().
>>
>> Changes since v2:
>>   * Added ipc_dev_cmd API support.
>>
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index da2d9ba..724ee696 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
>>   	  with the OS-image for the device. This option supplies the missing
>>   	  information. Enable this for x86 tablets with Silead touchscreens.
>>
>> +config INTEL_IPC_DEV
>> +	bool "Intel IPC Device Driver"
>> +	depends on X86_64
>> +	---help---
>> +	  This driver implements core features of Intel IPC device. Devices
>> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
>> +	  driver to implement IPC protocol of their respective device.
>> +
>>   endif # X86_PLATFORM_DEVICES
>>
>>   config PMC_ATOM
>> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
>> index 2b315d0..99a1af1 100644
>> --- a/drivers/platform/x86/Makefile
>> +++ b/drivers/platform/x86/Makefile
>> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
>> pmc_atom.o
>>   obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
>>   obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
>>   obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
>> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
>> diff --git a/drivers/platform/x86/intel_ipc_dev.c
>> b/drivers/platform/x86/intel_ipc_dev.c
>> new file mode 100644
>> index 0000000..f55ddec
>> --- /dev/null
>> +++ b/drivers/platform/x86/intel_ipc_dev.c
>> @@ -0,0 +1,576 @@
>> +/*
>> + * intel_ipc_dev.c: Intel IPC device class driver
>> + *
>> + * (C) Copyright 2017 Intel Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; version 2
>> + * of the License.
>> + *
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/module.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/idr.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +#include <linux/regmap.h>
>> +
>> +/* mutex to sync different ipc devices in same channel */ static struct
>> +mutex channel_lock[IPC_CHANNEL_MAX];
>> +
>> +static char *ipc_err_sources[] = {
>> +	[IPC_DEV_ERR_NONE] =
>> +		"No error",
>> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
>> +		"Command not-supported/Invalid",
>> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
>> +		"Command not-serviced/Invalid param",
>> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
>> +		"Unable-to-service/Cmd-timeout",
>> +	[IPC_DEV_ERR_CMD_INVALID] =
>> +		"Command-invalid/Cmd-locked",
>> +	[IPC_DEV_ERR_CMD_FAILED] =
>> +		"Command-failed/Invalid-VR-id",
>> +	[IPC_DEV_ERR_EMSECURITY] =
>> +		"Invalid Battery/VR-Error",
>> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
>> +		"Unsigned kernel",
>> +};
>> +
>> +static void ipc_channel_lock_init(void) {
>> +	int i;
>> +
>> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
>> +		mutex_init(&channel_lock[i]);
>> +}
>> +
>> +static struct class intel_ipc_class = {
>> +	.name = "intel_ipc",
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
>> +	int chan_type;
>> +
>> +	if (!ipc_dev || !ipc_dev->cfg)
>> +		return -ENODEV;
>> +
>> +	chan_type = ipc_dev->cfg->chan_type;
>> +	if (chan_type > IPC_CHANNEL_MAX)
>> +		return -EINVAL;
>> +
>> +	/* acquire channel lock */
>> +	mutex_lock(&channel_lock[chan_type]);
>> +
>> +	/* acquire IPC device lock */
>> +	mutex_lock(&ipc_dev->lock);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
>> +	int chan_type;
>> +
>> +	if (!ipc_dev || !ipc_dev->cfg)
>> +		return -ENODEV;
>> +
>> +	chan_type = ipc_dev->cfg->chan_type;
>> +	if (chan_type > IPC_CHANNEL_MAX)
>> +		return -EINVAL;
>> +
>> +	/* release IPC device lock */
>> +	mutex_unlock(&ipc_dev->lock);
>> +
>> +	/* release channel lock */
>> +	mutex_unlock(&channel_lock[chan_type]);
>> +
>> +	return 0;
>> +}
>> +
>> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
>> +	int error)
>> +{
>> +	if (error < IPC_DEV_ERR_MAX)
>> +		return ipc_err_sources[error];
>> +
>> +	return "Unknown Command";
>> +}
>> +
>> +/* Helper function to send given command to IPC device */ static inline
>> +void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
>> +	ipc_dev->cmd = cmd;
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
>> +		reinit_completion(&ipc_dev->cmd_complete);
>> +
>> +	if (ipc_dev->ops->enable_msi)
>> +		cmd = ipc_dev->ops->enable_msi(cmd);
>> +
>> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
>> }
>> +
>> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
>> +	int status;
>> +
>> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
>> +&status);
>> +
>> +	if (ipc_dev->ops->busy_check)
>> +		return ipc_dev->ops->busy_check(status);
>> +
>> +	return 0;
>> +}
>> +
>> +/* Check the status of IPC command and return err code if failed */
>> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
>> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
>> +	int status;
>> +	int ret = 0;
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
>> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
>> +				IPC_DEV_CMD_TIMEOUT))
>> +			ret = -ETIMEDOUT;
>> +	} else {
>> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
>> +			udelay(1);
>> +		if (!loop_count)
>> +			ret = -ETIMEDOUT;
>> +	}
>> +
>> +	if (ret < 0) {
>> +		dev_err(&ipc_dev->dev,
>> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
>>> cmd);
>> +		return ret;
>> +	}
>> +
>> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
>> +&status);
>> +
>> +	if (ipc_dev->ops->to_err_code)
>> +		ret = ipc_dev->ops->to_err_code(status);
>> +
>> +	if (ret) {
>> +		dev_err(&ipc_dev->dev,
>> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
>> +				ipc_dev_err_string(ipc_dev, ret),
>> +				status, ipc_dev->cmd);
>> +		return -EIO;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * ipc_dev_simple_cmd() - Send simple IPC command
>> + * @ipc_dev     : Reference to ipc device.
>> + * @cmd_list    : IPC command list.
>> + * @cmdlen      : Number of cmd/sub-cmds.
>> + *
>> + * Send a simple IPC command to ipc device.
>> + * Use this when don't need to specify input/output data
>> + * and source/dest pointers.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
>> +
>> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen)
>> +{
>> +	int ret;
>> +
>> +	if (!cmd_list)
>> +		return -EINVAL;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler */
>> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
>> +
>> +/**
>> + * ipc_dev_cmd() - Send IPC command with data.
>> + * @ipc_dev     : Reference to ipc_dev.
>> + * @cmd_list    : Array of commands/sub-commands.
>> + * @cmdlen      : Number of commands.
>> + * @in          : Input data of this IPC command.
>> + * @inlen       : Input data length in dwords.
>> + * @out         : Output data of this IPC command.
>> + * @outlen      : Length of output data in dwords.
>> + *
>> + * Send an IPC command to device with input/output data.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
>> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
>> +	int ret;
>> +
>> +	if (!cmd_list || !in)
>> +		return -EINVAL;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler. */
>> +	if (ipc_dev->ops->pre_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
>> +				out, outlen);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	/* Write inlen dwords of data to wrbuf_reg. */
>> +	if (inlen > 0)
>> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +	/* Read outlen dwords of data from rbug_reg. */
>> +	if (!ret && outlen > 0)
>> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->rbuf_reg, out, outlen);
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
>> +
>> +/**
>> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
>> + * @ipc_dev     : Reference to ipc_dev.
>> + * @cmd_list    : Array of commands/sub-commands.
>> + * @cmdlen      : Number of commands.
>> + * @in          : Input data of this IPC command.
>> + * @inlen       : Input data length in bytes.
>> + * @out         : Output data of this IPC command.
>> + * @outlen      : Length of output data in dwords.
>> + * @dptr        : IPC destination data address.
>> + * @sptr        : IPC source data address.
>> + *
>> + * Send an IPC command to device with input/output data and
>> + * source/dest pointers.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
> Sorry for coming in so late but since we are refactoring the API anyways, isn't it better to reduce the signature? Nine parameters is an awful lot and prone to errors and difficult to debug. (We found it out the hard way when we started using this a few years ago.)
I agree. Initially I thought of adding a command structure just like you 
mentioned. But finally decided not to do it because,

1. Not all drivers uses all parameters of this API. Most of them pass 
0,0 for DPTR and SPTR pointers. So the last two arguments are almost not 
used.
2. Adding a new structure requires all users of this API to add buffer 
code / some additional call to initialize the command structure which in 
turn makes the code look bit complex.

So I am not really sure whether it add any value. But if its the 
recommended approach then I will make that modification.
> This can be consolidated into a few neat structs, e.g.,:
> int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd,
> 		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)
>
> Same for the ipc_dev_cmd() APIs above as well.
>
>> +
>> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
>> cmdlen,
>> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
>> +	int ret;
>> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
>> +	u32 *inbuf;
>> +
>> +	if (!cmd_list || !in)
>> +		return -EINVAL;
>> +
>> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
>> +	if (!inbuf)
>> +		return -ENOMEM;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler. */
>> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
>> inlen,
>> +				out, outlen, dptr, sptr);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	/* If supported, write DPTR register.*/
>> +	if (ipc_dev->cfg->support_dptr)
>> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
>>> dptr_reg,
>> +				dptr);
>> +
>> +	/* If supported, write SPTR register. */
>> +	if (ipc_dev->cfg->support_sptr)
>> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
>>> sptr_reg,
>> +				sptr);
>> +
>> +	memcpy(inbuf, in, inlen);
>> +
>> +	/* Write inlen dwords of data to wrbuf_reg. */
>> +	if (inlen > 0)
>> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +	/* Read outlen dwords of data from rbug_reg. */
>> +	if (!ret && outlen > 0)
>> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->rbuf_reg, out, outlen);
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +	kfree(inbuf);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
>> +
>> +/* sysfs option to send simple IPC commands from userspace */ static
>> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
>> +				     struct device_attribute *attr,
>> +				     const char *buf, size_t count) {
>> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
>> +	u32 cmd;
>> +	int ret;
>> +
>> +	ret = sscanf(buf, "%d", &cmd);
>> +	if (ret != 1) {
>> +		dev_err(dev, "Error args\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
>> +	if (ret) {
>> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
>> +		return ret;
>> +	}
>> +	return (ssize_t)count;
>> +}
>> +
>> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
>> +
>> +static struct attribute *ipc_dev_attrs[] = {
>> +	&dev_attr_send_cmd.attr,
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group ipc_dev_group = {
>> +	.attrs = ipc_dev_attrs,
>> +};
>> +
>> +static const struct attribute_group *ipc_dev_groups[] = {
>> +	&ipc_dev_group,
>> +	NULL,
>> +};
>> +
>> +/* IPC device IRQ handler */
>> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
>> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
>> +
>> +	if (ipc_dev->ops->pre_irq_handler_fn)
>> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
>> +
>> +	complete(&ipc_dev->cmd_complete);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
>> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
>> +
>> +	if (!ipc_dev)
>> +		return;
>> +
>> +	device_del(&ipc_dev->dev);
>> +
>> +	kfree(ipc_dev);
>> +}
>> +
>> +static int match_name(struct device *dev, const void *data) {
>> +        if (!dev_name(dev))
>> +                return 0;
>> +
>> +        return !strcmp(dev_name(dev), (char *)data); }
>> +
>> +/**
>> + * intel_ipc_dev_get() - Get Intel IPC device from name.
>> + * @dev_name    : Name of the IPC device.
>> + *
>> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
>> + */
>> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
>> +        struct device *dev;
>> +
>> +	if (!dev_name)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
>> match_name);
>> +
>> +	return dev ? dev_get_drvdata(dev) : NULL; }
>> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
>> +
>> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
>> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
>> +
>> +/**
>> + * devm_intel_ipc_dev_get() - Resource managed version of
>> intel_ipc_dev_get().
>> + * @dev         : Device pointer.
>> + * @dev_name    : Name of the IPC device.
>> + *
>> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
>> + */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
>> +					const char *dev_name)
>> +{
>> +	struct intel_ipc_dev **ptr, *ipc_dev;
>> +
>> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
>> GFP_KERNEL);
>> +	if (!ptr)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	ipc_dev = intel_ipc_dev_get(dev_name);
>> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
>> +		*ptr = ipc_dev;
>> +		devres_add(dev, ptr);
>> +	} else {
>> +		devres_free(ptr);
>> +	}
>> +
>> +	return ipc_dev;
>> +}
>> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
>> +
>> +/**
>> + * devm_intel_ipc_dev_create() - Create IPC device
>> + * @dev		: IPC parent device.
>> + * @devname	: Name of the IPC device.
>> + * @cfg		: IPC device configuration.
>> + * @ops		: IPC device ops.
>> + *
>> + * Resource managed API to create IPC device with
>> + * given configuration.
>> + *
>> + * Return	: IPC device pointer or ERR_PTR(error code).
>> + */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
>> +		const char *devname,
>> +		struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops)
>> +{
>> +	struct intel_ipc_dev **ptr, *ipc_dev;
>> +	int ret;
>> +
>> +	if (!dev && !devname && !cfg)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	if (intel_ipc_dev_get(devname)) {
>> +		dev_err(dev, "IPC device %s already exist\n", devname);
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
>> +			GFP_KERNEL);
>> +	if (!ptr)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
>> +	if (!ipc_dev) {
>> +		ret = -ENOMEM;
>> +		goto err_dev_create;
>> +	}
>> +
>> +	ipc_dev->dev.class = &intel_ipc_class;
>> +	ipc_dev->dev.parent = dev;
>> +	ipc_dev->dev.groups = ipc_dev_groups;
>> +	ipc_dev->cfg = cfg;
>> +	ipc_dev->ops = ops;
>> +
>> +	mutex_init(&ipc_dev->lock);
>> +	init_completion(&ipc_dev->cmd_complete);
>> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
>> +	dev_set_name(&ipc_dev->dev, devname);
>> +	device_initialize(&ipc_dev->dev);
>> +
>> +	ret = device_add(&ipc_dev->dev);
>> +	if (ret < 0) {
>> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
>> +				__func__);
>> +		ret = -ENODEV;
>> +		goto err_dev_add;
>> +	}
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
>> +		if (devm_request_irq(&ipc_dev->dev,
>> +				ipc_dev->cfg->irq,
>> +				ipc_dev_irq_handler,
>> +				ipc_dev->cfg->irqflags,
>> +				dev_name(&ipc_dev->dev),
>> +				ipc_dev)) {
>> +			dev_err(&ipc_dev->dev,
>> +					"Failed to request irq\n");
>> +			goto err_irq_request;
>> +		}
>> +	}
>> +
>> +	*ptr = ipc_dev;
>> +
>> +	devres_add(dev, ptr);
>> +
>> +	return ipc_dev;
>> +
>> +err_irq_request:
>> +	device_del(&ipc_dev->dev);
>> +err_dev_add:
>> +	kfree(ipc_dev);
>> +err_dev_create:
>> +	devres_free(ptr);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
>> +
>> +static int __init intel_ipc_init(void)
>> +{
>> +	ipc_channel_lock_init();
>> +	return class_register(&intel_ipc_class); }
>> +
>> +static void __exit intel_ipc_exit(void) {
>> +	class_unregister(&intel_ipc_class);
>> +}
>> +subsys_initcall(intel_ipc_init);
>> +module_exit(intel_ipc_exit);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Kuppuswamy
>> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
>> +MODULE_DESCRIPTION("Intel IPC device class driver");
>> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
>> b/include/linux/platform_data/x86/intel_ipc_dev.h
>> new file mode 100644
>> index 0000000..eaeedaf
>> --- /dev/null
>> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
>> @@ -0,0 +1,206 @@
>> +/*
>> + * Intel IPC class device header file.
>> + *
>> + * (C) Copyright 2017 Intel Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; version 2
>> + * of the License.
>> + *
>> + */
>> +
>> +#ifndef INTEL_IPC_DEV_H
>> +#define INTEL_IPC_DEV_H
>> +
>> +#include <linux/module.h>
>> +#include <linux/device.h>
>> +
>> +/* IPC channel type */
>> +#define IPC_CHANNEL_IA_PMC                      0
>> +#define IPC_CHANNEL_IA_PUNIT                    1
>> +#define IPC_CHANNEL_PMC_PUNIT                   2
>> +#define IPC_CHANNEL_IA_SCU                      3
>> +#define IPC_CHANNEL_MAX                         4
>> +
>> +/* IPC return code */
>> +#define IPC_DEV_ERR_NONE			0
>> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
>> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
>> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
>> +#define IPC_DEV_ERR_CMD_INVALID			4
>> +#define IPC_DEV_ERR_CMD_FAILED			5
>> +#define IPC_DEV_ERR_EMSECURITY			6
>> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
>> +#define IPC_DEV_ERR_MAX				8
>> +
>> +/* IPC mode */
>> +#define IPC_DEV_MODE_IRQ			0
>> +#define IPC_DEV_MODE_POLLING			1
>> +
>> +/* IPC dev constants */
>> +#define IPC_DEV_CMD_LOOP_CNT			3000000
>> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
>> +#define IPC_DEV_DATA_BUFFER_SIZE		16
>> +
>> +struct intel_ipc_dev;
>> +struct intel_ipc_raw_cmd;
>> +
>> +/**
>> + * struct intel_ipc_dev_cfg - IPC device config structure.
>> + *
>> + * IPC device drivers uses the following config options to
>> + * register new IPC device.
>> + *
>> + * @cmd_regs            : IPC device command base regmap.
>> + * @data_regs           : IPC device data base regmap.
>> + * @wrbuf_reg           : IPC device data write register address.
>> + * @rbuf_reg            : IPC device data read register address.
>> + * @sptr_reg            : IPC device source data pointer register address.
>> + * @dptr_reg            : IPC device destination data pointer register
>> + *                        address.
>> + * @status_reg          : IPC command status register address.
>> + * @cmd_reg             : IPC command register address.
>> + * @mode                : IRQ/POLLING mode.
>> + * @irq                 : IPC device IRQ number.
>> + * @irqflags            : IPC device IRQ flags.
>> + * @chan_type           : IPC device channel type(PMC/PUNIT).
>> + * @msi                 : Enable/Disable MSI for IPC commands.
>> + * @support_dptr        : Support DPTR update.
>> + * @support_sptr        : Support SPTR update.
>> + *
>> + */
>> +struct intel_ipc_dev_cfg {
>> +	struct regmap *cmd_regs;
>> +	struct regmap *data_regs;
>> +	unsigned int wrbuf_reg;
>> +	unsigned int rbuf_reg;
>> +	unsigned int sptr_reg;
>> +	unsigned int dptr_reg;
>> +	unsigned int status_reg;
>> +	unsigned int cmd_reg;
>> +	int mode;
>> +	int irq;
>> +	int irqflags;
>> +	int chan_type;
>> +	bool use_msi;
>> +	bool support_dptr;
>> +	bool support_sptr;
>> +};
>> +
>> +/**
>> + * struct intel_ipc_dev_ops - IPC device ops structure.
>> + *
>> + * Call backs for IPC device specific operations.
>> + *
>> + * @to_err_code         : Status to error code conversion function.
>> + * @busy_check          : Check for IPC busy status.
>> + * @enable_msi          : Enable MSI for IPC commands.
>> + * @pre_simple_cmd_fn   : Custom pre-processing function for
>> + *                        ipc_dev_simple_cmd()
>> + * @pre_cmd_fn          : Custom pre-processing function for
>> + *                        ipc_dev_cmd()
>> + * @pre_raw_cmd_fn      : Custom pre-processing function for
>> + *                        ipc_dev_raw_cmd()
>> + *
>> + */
>> +struct intel_ipc_dev_ops {
>> +	int (*to_err_code)(int status);
>> +	int (*busy_check)(int status);
>> +	u32 (*enable_msi)(u32 cmd);
>> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
>> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
>> +			u32 *out, u32 outlen);
>> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
>> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
>> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq); };
>> +
>> +/**
>> + * struct intel_ipc_dev - Intel IPC device structure.
>> + *
>> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
>> + *
>> + * @dev                 : IPC device object.
>> + * @cmd                 : Current IPC device command.
>> + * @cmd_complete        : Command completion object.
>> + * @lock                : Lock to protect IPC device structure.
>> + * @ops                 : IPC device ops pointer.
>> + * @cfg                 : IPC device cfg pointer.
>> + *
>> + */
>> +struct intel_ipc_dev {
>> +	struct device dev;
>> +	int cmd;
>> +	struct completion cmd_complete;
>> +	struct mutex lock;
>> +	struct intel_ipc_dev_ops *ops;
>> +	struct intel_ipc_dev_cfg *cfg;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
>> +
>> +/* API to create new IPC device */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
>> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops);
>> +
>> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen);
>> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
>> ipc_dev_raw_cmd(struct
>> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
>> struct
>> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
>> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
>> +					const char *dev_name);
>> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
>> +	put_device(&ipc_dev->dev);
>> +}
>> +#else
>> +
>> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
>> +		struct device *dev,
>> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops)
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
>> +		u32 *cmd_list, u32 cmdlen)
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
>> +	return -EINVAL;
>> +}
>> +
>> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
>> *cmd_list,
>> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
>> +		u32 dptr, u32 sptr);
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
>> +*dev_name) {
>> +	return NULL;
>> +}
>> +
>> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device
>> *dev,
>> +					const char *dev_name);
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
>> +	return NULL;
>> +}
>> +#endif /* CONFIG_INTEL_IPC_DEV */
>> +#endif /* INTEL_IPC_DEV_H */
>> --
>> 2.7.4
>

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-10 22:09       ` sathyanarayanan kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10 22:09 UTC (permalink / raw)
  To: Chakravarty, Souvik K, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

Hi,


On 10/08/2017 09:53 PM, Chakravarty, Souvik K wrote:
>> From: sathyanarayanan.kuppuswamy@linux.intel.com
>> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
>> Sent: Sunday, October 8, 2017 3:50 AM
>> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
>> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
>> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
>> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
>> Souvik K <souvik.k.chakravarty@intel.com>
>> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
>> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
>> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
>>
>> From: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c redundantly
>> implements the same IPC features and has lot of code duplication between
>> them. This driver addresses this issue by grouping the common IPC
>> functionalities under the same driver.
>>
>> Signed-off-by: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> ---
>>   drivers/platform/x86/Kconfig                    |   8 +
>>   drivers/platform/x86/Makefile                   |   1 +
>>   drivers/platform/x86/intel_ipc_dev.c            | 576
>> ++++++++++++++++++++++++
>>   include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
>>   4 files changed, 791 insertions(+)
>>   create mode 100644 drivers/platform/x86/intel_ipc_dev.c
>>   create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
>>
>> Changes since v4:
>>   * None
>>
>> Changes since v3:
>>   * Fixed NULL pointer exception in intel_ipc_dev_get().
>>   * Fixed error in check for duplicate intel_ipc_dev.
>>   * Added custom interrupt handler support.
>>   * Used char array for error string conversion.
>>   * Added put dev support.
>>   * Added devm_* variant of intel_ipc_dev_get().
>>
>> Changes since v2:
>>   * Added ipc_dev_cmd API support.
>>
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index da2d9ba..724ee696 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
>>   	  with the OS-image for the device. This option supplies the missing
>>   	  information. Enable this for x86 tablets with Silead touchscreens.
>>
>> +config INTEL_IPC_DEV
>> +	bool "Intel IPC Device Driver"
>> +	depends on X86_64
>> +	---help---
>> +	  This driver implements core features of Intel IPC device. Devices
>> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
>> +	  driver to implement IPC protocol of their respective device.
>> +
>>   endif # X86_PLATFORM_DEVICES
>>
>>   config PMC_ATOM
>> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
>> index 2b315d0..99a1af1 100644
>> --- a/drivers/platform/x86/Makefile
>> +++ b/drivers/platform/x86/Makefile
>> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
>> pmc_atom.o
>>   obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
>>   obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
>>   obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
>> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
>> diff --git a/drivers/platform/x86/intel_ipc_dev.c
>> b/drivers/platform/x86/intel_ipc_dev.c
>> new file mode 100644
>> index 0000000..f55ddec
>> --- /dev/null
>> +++ b/drivers/platform/x86/intel_ipc_dev.c
>> @@ -0,0 +1,576 @@
>> +/*
>> + * intel_ipc_dev.c: Intel IPC device class driver
>> + *
>> + * (C) Copyright 2017 Intel Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; version 2
>> + * of the License.
>> + *
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/module.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/idr.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +#include <linux/regmap.h>
>> +
>> +/* mutex to sync different ipc devices in same channel */ static struct
>> +mutex channel_lock[IPC_CHANNEL_MAX];
>> +
>> +static char *ipc_err_sources[] = {
>> +	[IPC_DEV_ERR_NONE] =
>> +		"No error",
>> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
>> +		"Command not-supported/Invalid",
>> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
>> +		"Command not-serviced/Invalid param",
>> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
>> +		"Unable-to-service/Cmd-timeout",
>> +	[IPC_DEV_ERR_CMD_INVALID] =
>> +		"Command-invalid/Cmd-locked",
>> +	[IPC_DEV_ERR_CMD_FAILED] =
>> +		"Command-failed/Invalid-VR-id",
>> +	[IPC_DEV_ERR_EMSECURITY] =
>> +		"Invalid Battery/VR-Error",
>> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
>> +		"Unsigned kernel",
>> +};
>> +
>> +static void ipc_channel_lock_init(void) {
>> +	int i;
>> +
>> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
>> +		mutex_init(&channel_lock[i]);
>> +}
>> +
>> +static struct class intel_ipc_class = {
>> +	.name = "intel_ipc",
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
>> +	int chan_type;
>> +
>> +	if (!ipc_dev || !ipc_dev->cfg)
>> +		return -ENODEV;
>> +
>> +	chan_type = ipc_dev->cfg->chan_type;
>> +	if (chan_type > IPC_CHANNEL_MAX)
>> +		return -EINVAL;
>> +
>> +	/* acquire channel lock */
>> +	mutex_lock(&channel_lock[chan_type]);
>> +
>> +	/* acquire IPC device lock */
>> +	mutex_lock(&ipc_dev->lock);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
>> +	int chan_type;
>> +
>> +	if (!ipc_dev || !ipc_dev->cfg)
>> +		return -ENODEV;
>> +
>> +	chan_type = ipc_dev->cfg->chan_type;
>> +	if (chan_type > IPC_CHANNEL_MAX)
>> +		return -EINVAL;
>> +
>> +	/* release IPC device lock */
>> +	mutex_unlock(&ipc_dev->lock);
>> +
>> +	/* release channel lock */
>> +	mutex_unlock(&channel_lock[chan_type]);
>> +
>> +	return 0;
>> +}
>> +
>> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
>> +	int error)
>> +{
>> +	if (error < IPC_DEV_ERR_MAX)
>> +		return ipc_err_sources[error];
>> +
>> +	return "Unknown Command";
>> +}
>> +
>> +/* Helper function to send given command to IPC device */ static inline
>> +void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
>> +	ipc_dev->cmd = cmd;
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
>> +		reinit_completion(&ipc_dev->cmd_complete);
>> +
>> +	if (ipc_dev->ops->enable_msi)
>> +		cmd = ipc_dev->ops->enable_msi(cmd);
>> +
>> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
>> }
>> +
>> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
>> +	int status;
>> +
>> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
>> +&status);
>> +
>> +	if (ipc_dev->ops->busy_check)
>> +		return ipc_dev->ops->busy_check(status);
>> +
>> +	return 0;
>> +}
>> +
>> +/* Check the status of IPC command and return err code if failed */
>> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
>> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
>> +	int status;
>> +	int ret = 0;
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
>> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
>> +				IPC_DEV_CMD_TIMEOUT))
>> +			ret = -ETIMEDOUT;
>> +	} else {
>> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
>> +			udelay(1);
>> +		if (!loop_count)
>> +			ret = -ETIMEDOUT;
>> +	}
>> +
>> +	if (ret < 0) {
>> +		dev_err(&ipc_dev->dev,
>> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
>>> cmd);
>> +		return ret;
>> +	}
>> +
>> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
>> +&status);
>> +
>> +	if (ipc_dev->ops->to_err_code)
>> +		ret = ipc_dev->ops->to_err_code(status);
>> +
>> +	if (ret) {
>> +		dev_err(&ipc_dev->dev,
>> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
>> +				ipc_dev_err_string(ipc_dev, ret),
>> +				status, ipc_dev->cmd);
>> +		return -EIO;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * ipc_dev_simple_cmd() - Send simple IPC command
>> + * @ipc_dev     : Reference to ipc device.
>> + * @cmd_list    : IPC command list.
>> + * @cmdlen      : Number of cmd/sub-cmds.
>> + *
>> + * Send a simple IPC command to ipc device.
>> + * Use this when don't need to specify input/output data
>> + * and source/dest pointers.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
>> +
>> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen)
>> +{
>> +	int ret;
>> +
>> +	if (!cmd_list)
>> +		return -EINVAL;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler */
>> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
>> +
>> +/**
>> + * ipc_dev_cmd() - Send IPC command with data.
>> + * @ipc_dev     : Reference to ipc_dev.
>> + * @cmd_list    : Array of commands/sub-commands.
>> + * @cmdlen      : Number of commands.
>> + * @in          : Input data of this IPC command.
>> + * @inlen       : Input data length in dwords.
>> + * @out         : Output data of this IPC command.
>> + * @outlen      : Length of output data in dwords.
>> + *
>> + * Send an IPC command to device with input/output data.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
>> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
>> +	int ret;
>> +
>> +	if (!cmd_list || !in)
>> +		return -EINVAL;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler. */
>> +	if (ipc_dev->ops->pre_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
>> +				out, outlen);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	/* Write inlen dwords of data to wrbuf_reg. */
>> +	if (inlen > 0)
>> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +	/* Read outlen dwords of data from rbug_reg. */
>> +	if (!ret && outlen > 0)
>> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->rbuf_reg, out, outlen);
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
>> +
>> +/**
>> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
>> + * @ipc_dev     : Reference to ipc_dev.
>> + * @cmd_list    : Array of commands/sub-commands.
>> + * @cmdlen      : Number of commands.
>> + * @in          : Input data of this IPC command.
>> + * @inlen       : Input data length in bytes.
>> + * @out         : Output data of this IPC command.
>> + * @outlen      : Length of output data in dwords.
>> + * @dptr        : IPC destination data address.
>> + * @sptr        : IPC source data address.
>> + *
>> + * Send an IPC command to device with input/output data and
>> + * source/dest pointers.
>> + *
>> + * Return:	an IPC error code or 0 on success.
>> + */
> Sorry for coming in so late but since we are refactoring the API anyways, isn't it better to reduce the signature? Nine parameters is an awful lot and prone to errors and difficult to debug. (We found it out the hard way when we started using this a few years ago.)
I agree. Initially I thought of adding a command structure just like you 
mentioned. But finally decided not to do it because,

1. Not all drivers uses all parameters of this API. Most of them pass 
0,0 for DPTR and SPTR pointers. So the last two arguments are almost not 
used.
2. Adding a new structure requires all users of this API to add buffer 
code / some additional call to initialize the command structure which in 
turn makes the code look bit complex.

So I am not really sure whether it add any value. But if its the 
recommended approach then I will make that modification.
> This can be consolidated into a few neat structs, e.g.,:
> int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd,
> 		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)
>
> Same for the ipc_dev_cmd() APIs above as well.
>
>> +
>> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
>> cmdlen,
>> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
>> +	int ret;
>> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
>> +	u32 *inbuf;
>> +
>> +	if (!cmd_list || !in)
>> +		return -EINVAL;
>> +
>> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
>> +	if (!inbuf)
>> +		return -ENOMEM;
>> +
>> +	ret = ipc_dev_lock(ipc_dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Call custom pre-processing handler. */
>> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
>> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
>> inlen,
>> +				out, outlen, dptr, sptr);
>> +		if (ret)
>> +			goto unlock_device;
>> +	}
>> +
>> +	/* If supported, write DPTR register.*/
>> +	if (ipc_dev->cfg->support_dptr)
>> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
>>> dptr_reg,
>> +				dptr);
>> +
>> +	/* If supported, write SPTR register. */
>> +	if (ipc_dev->cfg->support_sptr)
>> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
>>> sptr_reg,
>> +				sptr);
>> +
>> +	memcpy(inbuf, in, inlen);
>> +
>> +	/* Write inlen dwords of data to wrbuf_reg. */
>> +	if (inlen > 0)
>> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
>> +
>> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
>> +
>> +	ret = ipc_dev_check_status(ipc_dev);
>> +
>> +	/* Read outlen dwords of data from rbug_reg. */
>> +	if (!ret && outlen > 0)
>> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
>> +				ipc_dev->cfg->rbuf_reg, out, outlen);
>> +unlock_device:
>> +	ipc_dev_unlock(ipc_dev);
>> +	kfree(inbuf);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
>> +
>> +/* sysfs option to send simple IPC commands from userspace */ static
>> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
>> +				     struct device_attribute *attr,
>> +				     const char *buf, size_t count) {
>> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
>> +	u32 cmd;
>> +	int ret;
>> +
>> +	ret = sscanf(buf, "%d", &cmd);
>> +	if (ret != 1) {
>> +		dev_err(dev, "Error args\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
>> +	if (ret) {
>> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
>> +		return ret;
>> +	}
>> +	return (ssize_t)count;
>> +}
>> +
>> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
>> +
>> +static struct attribute *ipc_dev_attrs[] = {
>> +	&dev_attr_send_cmd.attr,
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group ipc_dev_group = {
>> +	.attrs = ipc_dev_attrs,
>> +};
>> +
>> +static const struct attribute_group *ipc_dev_groups[] = {
>> +	&ipc_dev_group,
>> +	NULL,
>> +};
>> +
>> +/* IPC device IRQ handler */
>> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
>> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
>> +
>> +	if (ipc_dev->ops->pre_irq_handler_fn)
>> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
>> +
>> +	complete(&ipc_dev->cmd_complete);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
>> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
>> +
>> +	if (!ipc_dev)
>> +		return;
>> +
>> +	device_del(&ipc_dev->dev);
>> +
>> +	kfree(ipc_dev);
>> +}
>> +
>> +static int match_name(struct device *dev, const void *data) {
>> +        if (!dev_name(dev))
>> +                return 0;
>> +
>> +        return !strcmp(dev_name(dev), (char *)data); }
>> +
>> +/**
>> + * intel_ipc_dev_get() - Get Intel IPC device from name.
>> + * @dev_name    : Name of the IPC device.
>> + *
>> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
>> + */
>> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
>> +        struct device *dev;
>> +
>> +	if (!dev_name)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
>> match_name);
>> +
>> +	return dev ? dev_get_drvdata(dev) : NULL; }
>> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
>> +
>> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
>> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
>> +
>> +/**
>> + * devm_intel_ipc_dev_get() - Resource managed version of
>> intel_ipc_dev_get().
>> + * @dev         : Device pointer.
>> + * @dev_name    : Name of the IPC device.
>> + *
>> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
>> + */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
>> +					const char *dev_name)
>> +{
>> +	struct intel_ipc_dev **ptr, *ipc_dev;
>> +
>> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
>> GFP_KERNEL);
>> +	if (!ptr)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	ipc_dev = intel_ipc_dev_get(dev_name);
>> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
>> +		*ptr = ipc_dev;
>> +		devres_add(dev, ptr);
>> +	} else {
>> +		devres_free(ptr);
>> +	}
>> +
>> +	return ipc_dev;
>> +}
>> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
>> +
>> +/**
>> + * devm_intel_ipc_dev_create() - Create IPC device
>> + * @dev		: IPC parent device.
>> + * @devname	: Name of the IPC device.
>> + * @cfg		: IPC device configuration.
>> + * @ops		: IPC device ops.
>> + *
>> + * Resource managed API to create IPC device with
>> + * given configuration.
>> + *
>> + * Return	: IPC device pointer or ERR_PTR(error code).
>> + */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
>> +		const char *devname,
>> +		struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops)
>> +{
>> +	struct intel_ipc_dev **ptr, *ipc_dev;
>> +	int ret;
>> +
>> +	if (!dev && !devname && !cfg)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	if (intel_ipc_dev_get(devname)) {
>> +		dev_err(dev, "IPC device %s already exist\n", devname);
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
>> +			GFP_KERNEL);
>> +	if (!ptr)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
>> +	if (!ipc_dev) {
>> +		ret = -ENOMEM;
>> +		goto err_dev_create;
>> +	}
>> +
>> +	ipc_dev->dev.class = &intel_ipc_class;
>> +	ipc_dev->dev.parent = dev;
>> +	ipc_dev->dev.groups = ipc_dev_groups;
>> +	ipc_dev->cfg = cfg;
>> +	ipc_dev->ops = ops;
>> +
>> +	mutex_init(&ipc_dev->lock);
>> +	init_completion(&ipc_dev->cmd_complete);
>> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
>> +	dev_set_name(&ipc_dev->dev, devname);
>> +	device_initialize(&ipc_dev->dev);
>> +
>> +	ret = device_add(&ipc_dev->dev);
>> +	if (ret < 0) {
>> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
>> +				__func__);
>> +		ret = -ENODEV;
>> +		goto err_dev_add;
>> +	}
>> +
>> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
>> +		if (devm_request_irq(&ipc_dev->dev,
>> +				ipc_dev->cfg->irq,
>> +				ipc_dev_irq_handler,
>> +				ipc_dev->cfg->irqflags,
>> +				dev_name(&ipc_dev->dev),
>> +				ipc_dev)) {
>> +			dev_err(&ipc_dev->dev,
>> +					"Failed to request irq\n");
>> +			goto err_irq_request;
>> +		}
>> +	}
>> +
>> +	*ptr = ipc_dev;
>> +
>> +	devres_add(dev, ptr);
>> +
>> +	return ipc_dev;
>> +
>> +err_irq_request:
>> +	device_del(&ipc_dev->dev);
>> +err_dev_add:
>> +	kfree(ipc_dev);
>> +err_dev_create:
>> +	devres_free(ptr);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
>> +
>> +static int __init intel_ipc_init(void)
>> +{
>> +	ipc_channel_lock_init();
>> +	return class_register(&intel_ipc_class); }
>> +
>> +static void __exit intel_ipc_exit(void) {
>> +	class_unregister(&intel_ipc_class);
>> +}
>> +subsys_initcall(intel_ipc_init);
>> +module_exit(intel_ipc_exit);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Kuppuswamy
>> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
>> +MODULE_DESCRIPTION("Intel IPC device class driver");
>> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
>> b/include/linux/platform_data/x86/intel_ipc_dev.h
>> new file mode 100644
>> index 0000000..eaeedaf
>> --- /dev/null
>> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
>> @@ -0,0 +1,206 @@
>> +/*
>> + * Intel IPC class device header file.
>> + *
>> + * (C) Copyright 2017 Intel Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; version 2
>> + * of the License.
>> + *
>> + */
>> +
>> +#ifndef INTEL_IPC_DEV_H
>> +#define INTEL_IPC_DEV_H
>> +
>> +#include <linux/module.h>
>> +#include <linux/device.h>
>> +
>> +/* IPC channel type */
>> +#define IPC_CHANNEL_IA_PMC                      0
>> +#define IPC_CHANNEL_IA_PUNIT                    1
>> +#define IPC_CHANNEL_PMC_PUNIT                   2
>> +#define IPC_CHANNEL_IA_SCU                      3
>> +#define IPC_CHANNEL_MAX                         4
>> +
>> +/* IPC return code */
>> +#define IPC_DEV_ERR_NONE			0
>> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
>> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
>> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
>> +#define IPC_DEV_ERR_CMD_INVALID			4
>> +#define IPC_DEV_ERR_CMD_FAILED			5
>> +#define IPC_DEV_ERR_EMSECURITY			6
>> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
>> +#define IPC_DEV_ERR_MAX				8
>> +
>> +/* IPC mode */
>> +#define IPC_DEV_MODE_IRQ			0
>> +#define IPC_DEV_MODE_POLLING			1
>> +
>> +/* IPC dev constants */
>> +#define IPC_DEV_CMD_LOOP_CNT			3000000
>> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
>> +#define IPC_DEV_DATA_BUFFER_SIZE		16
>> +
>> +struct intel_ipc_dev;
>> +struct intel_ipc_raw_cmd;
>> +
>> +/**
>> + * struct intel_ipc_dev_cfg - IPC device config structure.
>> + *
>> + * IPC device drivers uses the following config options to
>> + * register new IPC device.
>> + *
>> + * @cmd_regs            : IPC device command base regmap.
>> + * @data_regs           : IPC device data base regmap.
>> + * @wrbuf_reg           : IPC device data write register address.
>> + * @rbuf_reg            : IPC device data read register address.
>> + * @sptr_reg            : IPC device source data pointer register address.
>> + * @dptr_reg            : IPC device destination data pointer register
>> + *                        address.
>> + * @status_reg          : IPC command status register address.
>> + * @cmd_reg             : IPC command register address.
>> + * @mode                : IRQ/POLLING mode.
>> + * @irq                 : IPC device IRQ number.
>> + * @irqflags            : IPC device IRQ flags.
>> + * @chan_type           : IPC device channel type(PMC/PUNIT).
>> + * @msi                 : Enable/Disable MSI for IPC commands.
>> + * @support_dptr        : Support DPTR update.
>> + * @support_sptr        : Support SPTR update.
>> + *
>> + */
>> +struct intel_ipc_dev_cfg {
>> +	struct regmap *cmd_regs;
>> +	struct regmap *data_regs;
>> +	unsigned int wrbuf_reg;
>> +	unsigned int rbuf_reg;
>> +	unsigned int sptr_reg;
>> +	unsigned int dptr_reg;
>> +	unsigned int status_reg;
>> +	unsigned int cmd_reg;
>> +	int mode;
>> +	int irq;
>> +	int irqflags;
>> +	int chan_type;
>> +	bool use_msi;
>> +	bool support_dptr;
>> +	bool support_sptr;
>> +};
>> +
>> +/**
>> + * struct intel_ipc_dev_ops - IPC device ops structure.
>> + *
>> + * Call backs for IPC device specific operations.
>> + *
>> + * @to_err_code         : Status to error code conversion function.
>> + * @busy_check          : Check for IPC busy status.
>> + * @enable_msi          : Enable MSI for IPC commands.
>> + * @pre_simple_cmd_fn   : Custom pre-processing function for
>> + *                        ipc_dev_simple_cmd()
>> + * @pre_cmd_fn          : Custom pre-processing function for
>> + *                        ipc_dev_cmd()
>> + * @pre_raw_cmd_fn      : Custom pre-processing function for
>> + *                        ipc_dev_raw_cmd()
>> + *
>> + */
>> +struct intel_ipc_dev_ops {
>> +	int (*to_err_code)(int status);
>> +	int (*busy_check)(int status);
>> +	u32 (*enable_msi)(u32 cmd);
>> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
>> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
>> +			u32 *out, u32 outlen);
>> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
>> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
>> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq); };
>> +
>> +/**
>> + * struct intel_ipc_dev - Intel IPC device structure.
>> + *
>> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
>> + *
>> + * @dev                 : IPC device object.
>> + * @cmd                 : Current IPC device command.
>> + * @cmd_complete        : Command completion object.
>> + * @lock                : Lock to protect IPC device structure.
>> + * @ops                 : IPC device ops pointer.
>> + * @cfg                 : IPC device cfg pointer.
>> + *
>> + */
>> +struct intel_ipc_dev {
>> +	struct device dev;
>> +	int cmd;
>> +	struct completion cmd_complete;
>> +	struct mutex lock;
>> +	struct intel_ipc_dev_ops *ops;
>> +	struct intel_ipc_dev_cfg *cfg;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
>> +
>> +/* API to create new IPC device */
>> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
>> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops);
>> +
>> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen);
>> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
>> ipc_dev_raw_cmd(struct
>> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
>> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
>> struct
>> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
>> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
>> +					const char *dev_name);
>> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
>> +	put_device(&ipc_dev->dev);
>> +}
>> +#else
>> +
>> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
>> +		struct device *dev,
>> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
>> +		struct intel_ipc_dev_ops *ops)
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
>> +		u32 *cmd_list, u32 cmdlen)
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
>> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
>> +	return -EINVAL;
>> +}
>> +
>> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
>> *cmd_list,
>> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
>> +		u32 dptr, u32 sptr);
>> +{
>> +	return -EINVAL;
>> +}
>> +
>> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
>> +*dev_name) {
>> +	return NULL;
>> +}
>> +
>> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device
>> *dev,
>> +					const char *dev_name);
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
>> +	return NULL;
>> +}
>> +#endif /* CONFIG_INTEL_IPC_DEV */
>> +#endif /* INTEL_IPC_DEV_H */
>> --
>> 2.7.4
>

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  2017-10-09  5:07     ` Chakravarty, Souvik K
@ 2017-10-10 22:28       ` sathyanarayanan kuppuswamy
  -1 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10 22:28 UTC (permalink / raw)
  To: Chakravarty, Souvik K, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid



On 10/08/2017 10:07 PM, Chakravarty, Souvik K wrote:
>> From: sathyanarayanan.kuppuswamy@linux.intel.com
>> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
>> Sent: Sunday, October 8, 2017 3:50 AM
>> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
>> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
>> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
>> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
>> Souvik K <souvik.k.chakravarty@intel.com>
>> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
>> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
>> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
>> device calls
>>
>> From: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Removed redundant IPC helper functions and refactored the driver to use
>> APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC user
>> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC driver.
>>
>> Signed-off-by: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> ---
>>   arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
>>   drivers/platform/x86/Kconfig                  |   1 +
>>   drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
>>   drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
>>   4 files changed, 223 insertions(+), 303 deletions(-)
>>
>> Changes since v4:
>>   * None
>>
>> Changes since v2:
>>   * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
>>   * Added intel_ipc_dev_put() support.
>>
>> Changes since v1:
>>   * Removed custom APIs.
>>   * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
>>     IPC driver.
>>
>> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
>> b/arch/x86/include/asm/intel_punit_ipc.h
>> index 201eb9d..cf1630c 100644
>> --- a/arch/x86/include/asm/intel_punit_ipc.h
>> +++ b/arch/x86/include/asm/intel_punit_ipc.h
>> @@ -1,10 +1,8 @@
>>   #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
>>   #define  _ASM_X86_INTEL_PUNIT_IPC_H_
>>
>> -/*
>> - * Three types of 8bit P-Unit IPC commands are supported,
>> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
>> - */
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +
>>   typedef enum {
>>   	BIOS_IPC = 0,
>>   	GTDRIVER_IPC,
>> @@ -12,61 +10,60 @@ typedef enum {
>>   	RESERVED_IPC,
>>   } IPC_TYPE;
>>
>> -#define IPC_TYPE_OFFSET			6
>> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
>> IPC_TYPE_OFFSET)
>> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
>> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
>> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
>> +#define PUNIT_PARAM_LEN				3
>>
>>   /* BIOS => Pcode commands */
>> -#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_BIOS_VR_INTERFACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_BIOS_READ_PCS
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x03)
>> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
>> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
>> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
>> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x07)
>> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
>> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
>> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
>> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
>> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
>> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
>> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
>> -#define IPC_PUNIT_BIOS_RESERVED
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
>> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
>> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
>> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
>> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
>> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
>> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
>> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
>> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
>> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
>> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
>> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
>> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
>> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
>> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
>> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
>> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
>> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
>> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
>> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
>> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
>> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
>> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
>> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
>> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
>> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
>> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
>> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
>> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
>> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
>> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
>> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
>> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
>>
>>   /* GT Driver => Pcode commands */
>> -#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_GTD_CONFIG
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
>> -#define IPC_PUNIT_GTD_GET_WM_VAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
>> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
>> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
>> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
>> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
>> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
>> +#define IPC_PUNIT_GTD_ZERO			(0x00)
>> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
>> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
>> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
>> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
>> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
>> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
>> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
>> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
>> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
>>
>>   /* ISP Driver => Pcode commands */
>> -#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_ISPD_CONFIG
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
>> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
>> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
>> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
>> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
>> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
>> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
>> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
>> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
>>
>>   /* Error codes */
>>   #define IPC_PUNIT_ERR_SUCCESS			0
>> @@ -77,25 +74,11 @@ typedef enum {
>>   #define IPC_PUNIT_ERR_INVALID_VR_ID		5
>>   #define IPC_PUNIT_ERR_VR_ERR			6
>>
>> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
>> -
>> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2); -int
>> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
>> *out);
>> -
>> -#else
>> -
>> -static inline int intel_punit_ipc_simple_command(int cmd,
>> -						  int para1, int para2)
>> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32
>> +param3)
>>   {
>> -	return -ENODEV;
>> +	cmd[0] = param1;
>> +	cmd[1] = param2;
>> +	cmd[2] = param3;
>>   }
>>
>> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
>> -					  u32 *in, u32 *out)
>> -{
>> -	return -ENODEV;
>> -}
>> -
>> -#endif /* CONFIG_INTEL_PUNIT_IPC */
>> -
>>   #endif
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index 724ee696..9442c23 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
>>
>>   config INTEL_PUNIT_IPC
>>   	tristate "Intel P-Unit IPC Driver"
>> +	select REGMAP_MMIO
>>   	---help---
>>   	  This driver provides support for Intel P-Unit Mailbox IPC
>> mechanism,
>>   	  which is used to bridge the communications between kernel and P-
>> Unit.
>> diff --git a/drivers/platform/x86/intel_punit_ipc.c
>> b/drivers/platform/x86/intel_punit_ipc.c
>> index b5b8901..f310a05 100644
>> --- a/drivers/platform/x86/intel_punit_ipc.c
>> +++ b/drivers/platform/x86/intel_punit_ipc.c
>> @@ -18,18 +18,18 @@
>>   #include <linux/device.h>
>>   #include <linux/interrupt.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +#include <linux/regmap.h>
>>   #include <asm/intel_punit_ipc.h>
>>
>> -/* IPC Mailbox registers */
>> -#define OFFSET_DATA_LOW		0x0
>> -#define OFFSET_DATA_HIGH	0x4
>>   /* bit field of interface register */
>>   #define	CMD_RUN			BIT(31)
>> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
>> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
>>   #define	CMD_PARA1_SHIFT		8
>>   #define	CMD_PARA2_SHIFT		16
>>
>> -#define CMD_TIMEOUT_SECONDS	1
>> +/* IPC PUNIT commands */
>> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
>> 0)
>>
>>   enum {
>>   	BASE_DATA = 0,
>> @@ -39,187 +39,42 @@ enum {
>>
>>   typedef struct {
>>   	struct device *dev;
>> -	struct mutex lock;
>> -	int irq;
>> -	struct completion cmd_complete;
>>   	/* base of interface and data registers */
>>   	void __iomem *base[RESERVED_IPC][BASE_MAX];
>> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
>>   	IPC_TYPE type;
>>   } IPC_DEV;
>>
>>   static IPC_DEV *punit_ipcdev;
>>
>> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_IFACE]);
>> -}
>> -
>> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd) -
>> {
>> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
>> -}
>> -
>> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
>> -}
>> -
>> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
>> -}
>> -
>> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32
>> data) -{
>> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
>> -}
>> -
>> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32
>> data) -{
>> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
>> -}
>> +const char *ipc_dev_name[RESERVED_IPC] = {
>> +	PUNIT_BIOS_IPC_DEV,
>> +	PUNIT_GTD_IPC_DEV,
>> +	PUNIT_ISP_IPC_DEV
>> +};
>>
>> -static const char *ipc_err_string(int error) -{
>> -	if (error == IPC_PUNIT_ERR_SUCCESS)
>> -		return "no error";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
>> -		return "invalid command";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
>> -		return "invalid parameter";
>> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
>> -		return "command timeout";
>> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
>> -		return "command locked";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
>> -		return "invalid vr id";
>> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
>> -		return "vr error";
>> -	else
>> -		return "unknown error";
>> -}
>> +static struct regmap_config punit_regmap_config = {
>> +        .reg_bits = 32,
>> +        .reg_stride = 4,
>> +        .val_bits = 32,
>> +};
>>
>> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
>> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>>   {
>> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
>> -	int errcode;
>> -	int status;
>> -
>> -	if (ipcdev->irq) {
>> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
>> -						 CMD_TIMEOUT_SECONDS *
>> HZ)) {
>> -			dev_err(ipcdev->dev, "IPC timed out\n");
>> -			return -ETIMEDOUT;
>> -		}
>> -	} else {
>> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
>> loops)
>> -			udelay(1);
>> -		if (!loops) {
>> -			dev_err(ipcdev->dev, "IPC timed out\n");
>> -			return -ETIMEDOUT;
>> -		}
>> -	}
>> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
>> +		return -EINVAL;
>>
>> -	status = ipc_read_status(ipcdev, type);
>> -	errcode = status & CMD_ERRCODE_MASK;
>> -	if (errcode) {
>> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
>> -			ipc_err_string(errcode), status);
>> -		return -EIO;
>> -	}
>> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
>> +		cmd_list[2] << CMD_PARA1_SHIFT;
>>
>>   	return 0;
>>   }
>>
>> -/**
>> - * intel_punit_ipc_simple_command() - Simple IPC command
>> - * @cmd:	IPC command code.
>> - * @para1:	First 8bit parameter, set 0 if not used.
>> - * @para2:	Second 8bit parameter, set 0 if not used.
>> - *
>> - * Send a IPC command to P-Unit when there is no data transaction
>> - *
>> - * Return:	IPC error code or 0 on success.
>> - */
>> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
>> -	IPC_DEV *ipcdev = punit_ipcdev;
>> -	IPC_TYPE type;
>> -	u32 val;
>> -	int ret;
>> -
>> -	mutex_lock(&ipcdev->lock);
>> -
>> -	reinit_completion(&ipcdev->cmd_complete);
>> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
>> -
>> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
>> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
>> CMD_PARA1_SHIFT;
>> -	ipc_write_cmd(ipcdev, type, val);
>> -	ret = intel_punit_ipc_check_status(ipcdev, type);
>> -
>> -	mutex_unlock(&ipcdev->lock);
>> -
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
>> -
>> -/**
>> - * intel_punit_ipc_command() - IPC command with data and pointers
>> - * @cmd:	IPC command code.
>> - * @para1:	First 8bit parameter, set 0 if not used.
>> - * @para2:	Second 8bit parameter, set 0 if not used.
>> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
>> and ISPD.
>> - * @out:	Output data.
>> - *
>> - * Send a IPC command to P-Unit with data transaction
>> - *
>> - * Return:	IPC error code or 0 on success.
>> - */
>> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
>> *out) -{
>> -	IPC_DEV *ipcdev = punit_ipcdev;
>> -	IPC_TYPE type;
>> -	u32 val;
>> -	int ret;
>> -
>> -	mutex_lock(&ipcdev->lock);
>> -
>> -	reinit_completion(&ipcdev->cmd_complete);
>> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
>> -
>> -	if (in) {
>> -		ipc_write_data_low(ipcdev, type, *in);
>> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
>> -			ipc_write_data_high(ipcdev, type, *++in);
>> -	}
>> -
>> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
>> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
>> CMD_PARA1_SHIFT;
>> -	ipc_write_cmd(ipcdev, type, val);
>> -
>> -	ret = intel_punit_ipc_check_status(ipcdev, type);
>> -	if (ret)
>> -		goto out;
>> -
>> -	if (out) {
>> -		*out = ipc_read_data_low(ipcdev, type);
>> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
>> -			*++out = ipc_read_data_high(ipcdev, type);
>> -	}
>> -
>> -out:
>> -	mutex_unlock(&ipcdev->lock);
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
>> -
>> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
>> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */ int
>> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
>> +		u32 outlen, u32 dptr, u32 sptr)
>>   {
>> -	IPC_DEV *ipcdev = dev_id;
>> -
>> -	complete(&ipcdev->cmd_complete);
>> -	return IRQ_HANDLED;
>> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
>>   }
>>
>>   static int intel_punit_get_bars(struct platform_device *pdev) @@ -282,9
>> +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
>>   	return 0;
>>   }
>>
>> +static int punit_ipc_err_code(int status) {
>> +	return (status & CMD_ERRCODE_MASK);
>> +}
>> +
>> +static int punit_ipc_busy_check(int status) {
>> +	return status | CMD_RUN;
>> +}
>> +
>> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
>> +		const char *devname,
>> +		int irq,
>> +		void __iomem *base,
>> +		void __iomem *data)
>> +{
>> +	struct intel_ipc_dev_ops *ops;
>> +	struct intel_ipc_dev_cfg *cfg;
>> +	struct regmap *cmd_regs, *data_regs;
>> +
>> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
>> +        if (!cfg)
>> +                return ERR_PTR(-ENOMEM);
>> +
>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>> +	if (!ops)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
>> "%s_%s",
>> +						  devname, "base");
>> +
>> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
>> +			&punit_regmap_config);
>> +	if (IS_ERR(cmd_regs)) {
>> +                dev_err(dev, "cmd_regs regmap init failed\n");
>> +                return ERR_CAST(cmd_regs);;
>> +        }
>> +
>> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
>> "%s_%s",
>> +						  devname, "data");
>> +
>> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
>> +			&punit_regmap_config);
>> +        if (IS_ERR(data_regs)) {
>> +                dev_err(dev, "data_regs regmap init failed\n");
>> +                return ERR_CAST(data_regs);;
>> +        }
>> +
>> +	/* set IPC dev ops */
>> +	ops->to_err_code = punit_ipc_err_code;
>> +	ops->busy_check = punit_ipc_busy_check;
>> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
>> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
>> +
>> +	if (irq > 0)
>> +	        cfg->mode = IPC_DEV_MODE_IRQ;
>> +	else
>> +	        cfg->mode = IPC_DEV_MODE_POLLING;
>> +
>> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
>> +	cfg->irq = irq;
>> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
>> +	cfg->cmd_regs = cmd_regs;
>> +	cfg->data_regs = data_regs;
>> +
>> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
>> +
>>   static int intel_punit_ipc_probe(struct platform_device *pdev)  {
>> -	int irq, ret;
>> +	int irq, ret, i;
>>
>>   	punit_ipcdev = devm_kzalloc(&pdev->dev,
>>   				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
>> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device
>> *pdev)
>>   	platform_set_drvdata(pdev, punit_ipcdev);
>>
>>   	irq = platform_get_irq(pdev, 0);
>> -	if (irq < 0) {
>> -		punit_ipcdev->irq = 0;
>> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
>> -	} else {
>> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
>> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
>> -				       &punit_ipcdev);
>> -		if (ret) {
>> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
>> irq);
>> -			return ret;
>> -		}
>> -		punit_ipcdev->irq = irq;
>> -	}
>>
>>   	ret = intel_punit_get_bars(pdev);
>>   	if (ret)
>> -		goto out;
>> +		return ret;
>> +
>> +	for (i = 0; i < RESERVED_IPC; i++) {
>> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
>> +				&pdev->dev,
>> +				ipc_dev_name[i],
>> +				irq,
>> +				punit_ipcdev->base[i][BASE_IFACE],
>> +				punit_ipcdev->base[i][BASE_DATA]);
>> +
>> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
>> +			dev_err(&pdev->dev, "%s create failed\n",
>> +					ipc_dev_name[i]);
>> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
>> +		}
>> +	}
>>
>>   	punit_ipcdev->dev = &pdev->dev;
>> -	mutex_init(&punit_ipcdev->lock);
>> -	init_completion(&punit_ipcdev->cmd_complete);
>>
>> -out:
>>   	return ret;
>> -}
>>
>> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
>> -	return 0;
>>   }
>>
>>   static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@ -332,7 +250,6
>> @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
>>
>>   static struct platform_driver intel_punit_ipc_driver = {
>>   	.probe = intel_punit_ipc_probe,
>> -	.remove = intel_punit_ipc_remove,
>>   	.driver = {
>>   		.name = "intel_punit_ipc",
>>   		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
>> a/drivers/platform/x86/intel_telemetry_pltdrv.c
>> b/drivers/platform/x86/intel_telemetry_pltdrv.c
>> index e0424d5..bf8284a 100644
>> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
>> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> This is a logical separation and so should be a different patch.
But if we split it into two different patches then it will break the 
bisect-ability of these patches.
>
>> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
>>
>>   static struct telemetry_plt_config *telm_conf;
>> +static struct intel_ipc_dev *punit_bios_ipc_dev;
> Simply punit_ipc_dev is good enough.
There are three PUNIT IPC devices. So to differentiate between them I 
added extra string to it.
>
>>   /*
>>    * The following counters are programmed by default during setup.
>> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
>>   	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
>>   };
>>
>> -
>>   static struct telemetry_evtmap
>>
>> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
>> NTS] = {
>>   	{"IA_CORE0_C6_RES",			0x0400},
>> @@ -283,13 +283,12 @@ static inline int
>> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline int
>> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
>>   	u32 write_buf;
>> -	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	write_buf = evt_id | TELEM_EVENT_ENABLE;
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
>> -				      index, 0, &write_buf, NULL);
>> -
>> -	return ret;
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
>> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
>>   }
> 1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
In my initial versions of this patch set, I had only exported 
ipc_dev_raw_cmd() API. Since this driver refactoring work was done 
during that time, I have used intel_dev_raw_cmd() API in it. I have 
added ipc_dev_cmd() API only recently  to handle some new requirements 
in intel_scu_ipc.c driver.

I will fix it in next version.

> 2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like
> telemetry_punit_send_cmd(). Same thoughout
I will make this change in next version.
>
>>   static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
>> evtconfig, @@ -435,6 +434,7 @@ static int
>> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
>>   	int ret, index, idx;
>>   	u32 *pss_evtmap;
>>   	u32 telem_ctrl;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	num_pss_evts = evtconfig.num_evts;
>>   	pss_period = evtconfig.period;
>> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>
>>   	/* PSS Config */
>>   	/* Get telemetry EVENT CTL */
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
>> -				      0, 0, NULL, &telem_ctrl);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN, NULL,
>> +			0, &telem_ctrl, 1, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Read Failed\n");
>>   		return ret;
>> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>
>>   	/* Disable Telemetry */
>>   	TELEM_DISABLE(telem_ctrl);
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				      0, 0, &telem_ctrl, NULL);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
>>   		return ret;
>> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   		/* Clear All Events */
>>   		TELEM_CLEAR_EVENTS(telem_ctrl);
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			return ret;
>> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   		/* Clear All Events */
>>   		TELEM_CLEAR_EVENTS(telem_ctrl);
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			return ret;
>> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   	TELEM_ENABLE_PERIODIC(telem_ctrl);
>>   	telem_ctrl |= pss_period;
>>
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				      0, 0, &telem_ctrl, NULL);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
>>   		return ret;
>> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
>> *pdev)  {
>>   	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
>>   	u32 read_buf, events, event_regs;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>   	int ret;
>>
>>   	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
>> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
>> telemetry_setup(struct platform_device *pdev)
>>   	telm_conf->ioss_config.max_period =
>> TELEM_MAX_PERIOD(read_buf);
>>
>>   	/* PUNIT Mailbox Setup */
>> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
>> 0, 0,
>> -				      NULL, &read_buf);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			NULL, 0, &read_buf, 1, 0, 0);
>>   	if (ret) {
>>   		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
>>   		return ret;
>> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)  {
>>   	u32 telem_ctrl = 0;
>>   	int ret = 0;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	mutex_lock(&(telm_conf->telem_lock));
>>   	if (ioss_period) {
>> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>   		}
>>
>>   		/* Get telemetry EVENT CTL */
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
>> -				0, 0, NULL, &telem_ctrl);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &telem_ctrl, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Read Failed\n");
>>   			goto out;
>> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>
>>   		/* Disable Telemetry */
>>   		TELEM_DISABLE(telem_ctrl);
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> +				0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			goto out;
>> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>   		TELEM_ENABLE_PERIODIC(telem_ctrl);
>>   		telem_ctrl |= pss_period;
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> +				0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Enable Write
>> Failed\n");
>>   			goto out;
>> @@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum
>> telemetry_unit telem_unit,  {
>>   	u32 temp = 0;
>>   	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	if (verbosity == NULL)
>>   		return -EINVAL;
>> @@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum
>> telemetry_unit telem_unit,
>>   	mutex_lock(&(telm_conf->telem_trace_lock));
>>   	switch (telem_unit) {
>>   	case TELEM_PSS:
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
>> -				0, 0, NULL, &temp);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &temp, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Read Failed\n");
>>   			goto out;
>> @@ -1058,15 +1071,16 @@ static int
>> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
>>   	u32 temp = 0;
>>   	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	verbosity &= TELEM_TRC_VERBOSITY_MASK;
>>
>>   	mutex_lock(&(telm_conf->telem_trace_lock));
>>   	switch (telem_unit) {
>>   	case TELEM_PSS:
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
>> -				0, 0, NULL, &temp);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &temp, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Read Failed\n");
>>   			goto out;
>> @@ -1074,10 +1088,10 @@ static int
>> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
>>
>>   		TELEM_CLEAR_VERBOSITY_BITS(temp);
>>   		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
>> -
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
>> -				0, 0, &temp, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> +				0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
>>   			goto out;
>> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
>> platform_device *pdev)
>>   	if (!id)
>>   		return -ENODEV;
>>
>> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
>> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
>> +		return PTR_ERR(punit_bios_ipc_dev);
>> +
>>   	telm_conf = (struct telemetry_plt_config *)id->driver_data;
>>
>>   	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
>> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device
>> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>>   	telemetry_clear_pltdata();
>> +	intel_ipc_dev_put(punit_bios_ipc_dev);
>>   	iounmap(telm_conf->pss_config.regmap);
>>   	iounmap(telm_conf->ioss_config.regmap);
>>
>> --
>> 2.7.4
>

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
@ 2017-10-10 22:28       ` sathyanarayanan kuppuswamy
  0 siblings, 0 replies; 53+ messages in thread
From: sathyanarayanan kuppuswamy @ 2017-10-10 22:28 UTC (permalink / raw)
  To: Chakravarty, Souvik K, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid



On 10/08/2017 10:07 PM, Chakravarty, Souvik K wrote:
>> From: sathyanarayanan.kuppuswamy@linux.intel.com
>> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
>> Sent: Sunday, October 8, 2017 3:50 AM
>> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
>> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
>> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
>> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org; Chakravarty,
>> Souvik K <souvik.k.chakravarty@intel.com>
>> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
>> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
>> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
>> device calls
>>
>> From: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>>
>> Removed redundant IPC helper functions and refactored the driver to use
>> APIs provided by generic IPC driver. This patch also cleans-up PUNIT IPC user
>> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC driver.
>>
>> Signed-off-by: Kuppuswamy Sathyanarayanan
>> <sathyanarayanan.kuppuswamy@linux.intel.com>
>> ---
>>   arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
>>   drivers/platform/x86/Kconfig                  |   1 +
>>   drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
>>   drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
>>   4 files changed, 223 insertions(+), 303 deletions(-)
>>
>> Changes since v4:
>>   * None
>>
>> Changes since v2:
>>   * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
>>   * Added intel_ipc_dev_put() support.
>>
>> Changes since v1:
>>   * Removed custom APIs.
>>   * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
>>     IPC driver.
>>
>> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
>> b/arch/x86/include/asm/intel_punit_ipc.h
>> index 201eb9d..cf1630c 100644
>> --- a/arch/x86/include/asm/intel_punit_ipc.h
>> +++ b/arch/x86/include/asm/intel_punit_ipc.h
>> @@ -1,10 +1,8 @@
>>   #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
>>   #define  _ASM_X86_INTEL_PUNIT_IPC_H_
>>
>> -/*
>> - * Three types of 8bit P-Unit IPC commands are supported,
>> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
>> - */
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +
>>   typedef enum {
>>   	BIOS_IPC = 0,
>>   	GTDRIVER_IPC,
>> @@ -12,61 +10,60 @@ typedef enum {
>>   	RESERVED_IPC,
>>   } IPC_TYPE;
>>
>> -#define IPC_TYPE_OFFSET			6
>> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
>> IPC_TYPE_OFFSET)
>> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
>> IPC_TYPE_OFFSET)
>> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
>> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
>> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
>> +#define PUNIT_PARAM_LEN				3
>>
>>   /* BIOS => Pcode commands */
>> -#define IPC_PUNIT_BIOS_ZERO			(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_BIOS_VR_INTERFACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_BIOS_READ_PCS
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_BIOS_WRITE_PCS		(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x03)
>> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
>> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
>> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
>> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(IPC_PUNIT_BIOS_CMD_BASE
>> | 0x07)
>> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
>> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
>> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
>> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
>> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
>> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
>> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
>> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
>> -#define IPC_PUNIT_BIOS_RESERVED
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
>> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
>> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
>> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
>> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
>> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
>> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
>> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
>> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
>> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
>> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
>> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
>> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
>> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
>> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
>> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
>> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
>> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
>> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
>> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
>> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
>> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
>> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
>> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
>> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
>> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
>> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
>> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
>> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
>> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
>> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
>> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
>> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
>> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
>> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
>>
>>   /* GT Driver => Pcode commands */
>> -#define IPC_PUNIT_GTD_ZERO			(IPC_PUNIT_GTD_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_GTD_CONFIG
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
>> -#define IPC_PUNIT_GTD_GET_WM_VAL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
>> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
>> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
>> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
>> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
>> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
>> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
>> +#define IPC_PUNIT_GTD_ZERO			(0x00)
>> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
>> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
>> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
>> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
>> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
>> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
>> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
>> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
>> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
>>
>>   /* ISP Driver => Pcode commands */
>> -#define IPC_PUNIT_ISPD_ZERO			(IPC_PUNIT_ISPD_CMD_BASE
>> | 0x00)
>> -#define IPC_PUNIT_ISPD_CONFIG
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
>> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
>> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
>> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
>> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
>> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
>> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
>> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
>> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
>> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
>> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
>> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
>>
>>   /* Error codes */
>>   #define IPC_PUNIT_ERR_SUCCESS			0
>> @@ -77,25 +74,11 @@ typedef enum {
>>   #define IPC_PUNIT_ERR_INVALID_VR_ID		5
>>   #define IPC_PUNIT_ERR_VR_ERR			6
>>
>> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
>> -
>> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2); -int
>> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
>> *out);
>> -
>> -#else
>> -
>> -static inline int intel_punit_ipc_simple_command(int cmd,
>> -						  int para1, int para2)
>> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2, u32
>> +param3)
>>   {
>> -	return -ENODEV;
>> +	cmd[0] = param1;
>> +	cmd[1] = param2;
>> +	cmd[2] = param3;
>>   }
>>
>> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
>> -					  u32 *in, u32 *out)
>> -{
>> -	return -ENODEV;
>> -}
>> -
>> -#endif /* CONFIG_INTEL_PUNIT_IPC */
>> -
>>   #endif
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index 724ee696..9442c23 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
>>
>>   config INTEL_PUNIT_IPC
>>   	tristate "Intel P-Unit IPC Driver"
>> +	select REGMAP_MMIO
>>   	---help---
>>   	  This driver provides support for Intel P-Unit Mailbox IPC
>> mechanism,
>>   	  which is used to bridge the communications between kernel and P-
>> Unit.
>> diff --git a/drivers/platform/x86/intel_punit_ipc.c
>> b/drivers/platform/x86/intel_punit_ipc.c
>> index b5b8901..f310a05 100644
>> --- a/drivers/platform/x86/intel_punit_ipc.c
>> +++ b/drivers/platform/x86/intel_punit_ipc.c
>> @@ -18,18 +18,18 @@
>>   #include <linux/device.h>
>>   #include <linux/interrupt.h>
>>   #include <linux/platform_device.h>
>> +#include <linux/platform_data/x86/intel_ipc_dev.h>
>> +#include <linux/regmap.h>
>>   #include <asm/intel_punit_ipc.h>
>>
>> -/* IPC Mailbox registers */
>> -#define OFFSET_DATA_LOW		0x0
>> -#define OFFSET_DATA_HIGH	0x4
>>   /* bit field of interface register */
>>   #define	CMD_RUN			BIT(31)
>> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
>> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
>>   #define	CMD_PARA1_SHIFT		8
>>   #define	CMD_PARA2_SHIFT		16
>>
>> -#define CMD_TIMEOUT_SECONDS	1
>> +/* IPC PUNIT commands */
>> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
>> 0)
>>
>>   enum {
>>   	BASE_DATA = 0,
>> @@ -39,187 +39,42 @@ enum {
>>
>>   typedef struct {
>>   	struct device *dev;
>> -	struct mutex lock;
>> -	int irq;
>> -	struct completion cmd_complete;
>>   	/* base of interface and data registers */
>>   	void __iomem *base[RESERVED_IPC][BASE_MAX];
>> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
>>   	IPC_TYPE type;
>>   } IPC_DEV;
>>
>>   static IPC_DEV *punit_ipcdev;
>>
>> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_IFACE]);
>> -}
>> -
>> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd) -
>> {
>> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
>> -}
>> -
>> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
>> -}
>> -
>> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
>> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
>> -}
>> -
>> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32
>> data) -{
>> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
>> -}
>> -
>> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32
>> data) -{
>> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
>> -}
>> +const char *ipc_dev_name[RESERVED_IPC] = {
>> +	PUNIT_BIOS_IPC_DEV,
>> +	PUNIT_GTD_IPC_DEV,
>> +	PUNIT_ISP_IPC_DEV
>> +};
>>
>> -static const char *ipc_err_string(int error) -{
>> -	if (error == IPC_PUNIT_ERR_SUCCESS)
>> -		return "no error";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
>> -		return "invalid command";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
>> -		return "invalid parameter";
>> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
>> -		return "command timeout";
>> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
>> -		return "command locked";
>> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
>> -		return "invalid vr id";
>> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
>> -		return "vr error";
>> -	else
>> -		return "unknown error";
>> -}
>> +static struct regmap_config punit_regmap_config = {
>> +        .reg_bits = 32,
>> +        .reg_stride = 4,
>> +        .val_bits = 32,
>> +};
>>
>> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
>> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
>>   {
>> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
>> -	int errcode;
>> -	int status;
>> -
>> -	if (ipcdev->irq) {
>> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
>> -						 CMD_TIMEOUT_SECONDS *
>> HZ)) {
>> -			dev_err(ipcdev->dev, "IPC timed out\n");
>> -			return -ETIMEDOUT;
>> -		}
>> -	} else {
>> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
>> loops)
>> -			udelay(1);
>> -		if (!loops) {
>> -			dev_err(ipcdev->dev, "IPC timed out\n");
>> -			return -ETIMEDOUT;
>> -		}
>> -	}
>> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
>> +		return -EINVAL;
>>
>> -	status = ipc_read_status(ipcdev, type);
>> -	errcode = status & CMD_ERRCODE_MASK;
>> -	if (errcode) {
>> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
>> -			ipc_err_string(errcode), status);
>> -		return -EIO;
>> -	}
>> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
>> +		cmd_list[2] << CMD_PARA1_SHIFT;
>>
>>   	return 0;
>>   }
>>
>> -/**
>> - * intel_punit_ipc_simple_command() - Simple IPC command
>> - * @cmd:	IPC command code.
>> - * @para1:	First 8bit parameter, set 0 if not used.
>> - * @para2:	Second 8bit parameter, set 0 if not used.
>> - *
>> - * Send a IPC command to P-Unit when there is no data transaction
>> - *
>> - * Return:	IPC error code or 0 on success.
>> - */
>> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
>> -	IPC_DEV *ipcdev = punit_ipcdev;
>> -	IPC_TYPE type;
>> -	u32 val;
>> -	int ret;
>> -
>> -	mutex_lock(&ipcdev->lock);
>> -
>> -	reinit_completion(&ipcdev->cmd_complete);
>> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
>> -
>> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
>> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
>> CMD_PARA1_SHIFT;
>> -	ipc_write_cmd(ipcdev, type, val);
>> -	ret = intel_punit_ipc_check_status(ipcdev, type);
>> -
>> -	mutex_unlock(&ipcdev->lock);
>> -
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
>> -
>> -/**
>> - * intel_punit_ipc_command() - IPC command with data and pointers
>> - * @cmd:	IPC command code.
>> - * @para1:	First 8bit parameter, set 0 if not used.
>> - * @para2:	Second 8bit parameter, set 0 if not used.
>> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
>> and ISPD.
>> - * @out:	Output data.
>> - *
>> - * Send a IPC command to P-Unit with data transaction
>> - *
>> - * Return:	IPC error code or 0 on success.
>> - */
>> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
>> *out) -{
>> -	IPC_DEV *ipcdev = punit_ipcdev;
>> -	IPC_TYPE type;
>> -	u32 val;
>> -	int ret;
>> -
>> -	mutex_lock(&ipcdev->lock);
>> -
>> -	reinit_completion(&ipcdev->cmd_complete);
>> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
>> -
>> -	if (in) {
>> -		ipc_write_data_low(ipcdev, type, *in);
>> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
>> -			ipc_write_data_high(ipcdev, type, *++in);
>> -	}
>> -
>> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
>> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
>> CMD_PARA1_SHIFT;
>> -	ipc_write_cmd(ipcdev, type, val);
>> -
>> -	ret = intel_punit_ipc_check_status(ipcdev, type);
>> -	if (ret)
>> -		goto out;
>> -
>> -	if (out) {
>> -		*out = ipc_read_data_low(ipcdev, type);
>> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
>> -			*++out = ipc_read_data_high(ipcdev, type);
>> -	}
>> -
>> -out:
>> -	mutex_unlock(&ipcdev->lock);
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
>> -
>> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
>> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */ int
>> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
>> +		u32 outlen, u32 dptr, u32 sptr)
>>   {
>> -	IPC_DEV *ipcdev = dev_id;
>> -
>> -	complete(&ipcdev->cmd_complete);
>> -	return IRQ_HANDLED;
>> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
>>   }
>>
>>   static int intel_punit_get_bars(struct platform_device *pdev) @@ -282,9
>> +137,77 @@ static int intel_punit_get_bars(struct platform_device *pdev)
>>   	return 0;
>>   }
>>
>> +static int punit_ipc_err_code(int status) {
>> +	return (status & CMD_ERRCODE_MASK);
>> +}
>> +
>> +static int punit_ipc_busy_check(int status) {
>> +	return status | CMD_RUN;
>> +}
>> +
>> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
>> +		const char *devname,
>> +		int irq,
>> +		void __iomem *base,
>> +		void __iomem *data)
>> +{
>> +	struct intel_ipc_dev_ops *ops;
>> +	struct intel_ipc_dev_cfg *cfg;
>> +	struct regmap *cmd_regs, *data_regs;
>> +
>> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
>> +        if (!cfg)
>> +                return ERR_PTR(-ENOMEM);
>> +
>> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
>> +	if (!ops)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
>> "%s_%s",
>> +						  devname, "base");
>> +
>> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
>> +			&punit_regmap_config);
>> +	if (IS_ERR(cmd_regs)) {
>> +                dev_err(dev, "cmd_regs regmap init failed\n");
>> +                return ERR_CAST(cmd_regs);;
>> +        }
>> +
>> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
>> "%s_%s",
>> +						  devname, "data");
>> +
>> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
>> +			&punit_regmap_config);
>> +        if (IS_ERR(data_regs)) {
>> +                dev_err(dev, "data_regs regmap init failed\n");
>> +                return ERR_CAST(data_regs);;
>> +        }
>> +
>> +	/* set IPC dev ops */
>> +	ops->to_err_code = punit_ipc_err_code;
>> +	ops->busy_check = punit_ipc_busy_check;
>> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
>> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
>> +
>> +	if (irq > 0)
>> +	        cfg->mode = IPC_DEV_MODE_IRQ;
>> +	else
>> +	        cfg->mode = IPC_DEV_MODE_POLLING;
>> +
>> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
>> +	cfg->irq = irq;
>> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
>> +	cfg->cmd_regs = cmd_regs;
>> +	cfg->data_regs = data_regs;
>> +
>> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
>> +
>>   static int intel_punit_ipc_probe(struct platform_device *pdev)  {
>> -	int irq, ret;
>> +	int irq, ret, i;
>>
>>   	punit_ipcdev = devm_kzalloc(&pdev->dev,
>>   				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
>> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct platform_device
>> *pdev)
>>   	platform_set_drvdata(pdev, punit_ipcdev);
>>
>>   	irq = platform_get_irq(pdev, 0);
>> -	if (irq < 0) {
>> -		punit_ipcdev->irq = 0;
>> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
>> -	} else {
>> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
>> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
>> -				       &punit_ipcdev);
>> -		if (ret) {
>> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
>> irq);
>> -			return ret;
>> -		}
>> -		punit_ipcdev->irq = irq;
>> -	}
>>
>>   	ret = intel_punit_get_bars(pdev);
>>   	if (ret)
>> -		goto out;
>> +		return ret;
>> +
>> +	for (i = 0; i < RESERVED_IPC; i++) {
>> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
>> +				&pdev->dev,
>> +				ipc_dev_name[i],
>> +				irq,
>> +				punit_ipcdev->base[i][BASE_IFACE],
>> +				punit_ipcdev->base[i][BASE_DATA]);
>> +
>> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
>> +			dev_err(&pdev->dev, "%s create failed\n",
>> +					ipc_dev_name[i]);
>> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
>> +		}
>> +	}
>>
>>   	punit_ipcdev->dev = &pdev->dev;
>> -	mutex_init(&punit_ipcdev->lock);
>> -	init_completion(&punit_ipcdev->cmd_complete);
>>
>> -out:
>>   	return ret;
>> -}
>>
>> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
>> -	return 0;
>>   }
>>
>>   static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@ -332,7 +250,6
>> @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
>>
>>   static struct platform_driver intel_punit_ipc_driver = {
>>   	.probe = intel_punit_ipc_probe,
>> -	.remove = intel_punit_ipc_remove,
>>   	.driver = {
>>   		.name = "intel_punit_ipc",
>>   		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
>> a/drivers/platform/x86/intel_telemetry_pltdrv.c
>> b/drivers/platform/x86/intel_telemetry_pltdrv.c
>> index e0424d5..bf8284a 100644
>> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
>> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> This is a logical separation and so should be a different patch.
But if we split it into two different patches then it will break the 
bisect-ability of these patches.
>
>> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
>>
>>   static struct telemetry_plt_config *telm_conf;
>> +static struct intel_ipc_dev *punit_bios_ipc_dev;
> Simply punit_ipc_dev is good enough.
There are three PUNIT IPC devices. So to differentiate between them I 
added extra string to it.
>
>>   /*
>>    * The following counters are programmed by default during setup.
>> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
>>   	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
>>   };
>>
>> -
>>   static struct telemetry_evtmap
>>
>> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
>> NTS] = {
>>   	{"IA_CORE0_C6_RES",			0x0400},
>> @@ -283,13 +283,12 @@ static inline int
>> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline int
>> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
>>   	u32 write_buf;
>> -	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	write_buf = evt_id | TELEM_EVENT_ENABLE;
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
>> -				      index, 0, &write_buf, NULL);
>> -
>> -	return ret;
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
>> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
>>   }
> 1) Why use raw_cmd here? It should use ipc_dev_cmd() according to original design. Same everywhere though this file.
In my initial versions of this patch set, I had only exported 
ipc_dev_raw_cmd() API. Since this driver refactoring work was done 
during that time, I have used intel_dev_raw_cmd() API in it. I have 
added ipc_dev_cmd() API only recently  to handle some new requirements 
in intel_scu_ipc.c driver.

I will fix it in next version.

> 2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time thoughout the file. They can be made into a separate local API inside telemetry, like
> telemetry_punit_send_cmd(). Same thoughout
I will make this change in next version.
>
>>   static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
>> evtconfig, @@ -435,6 +434,7 @@ static int
>> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
>>   	int ret, index, idx;
>>   	u32 *pss_evtmap;
>>   	u32 telem_ctrl;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	num_pss_evts = evtconfig.num_evts;
>>   	pss_period = evtconfig.period;
>> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>
>>   	/* PSS Config */
>>   	/* Get telemetry EVENT CTL */
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
>> -				      0, 0, NULL, &telem_ctrl);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN, NULL,
>> +			0, &telem_ctrl, 1, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Read Failed\n");
>>   		return ret;
>> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>
>>   	/* Disable Telemetry */
>>   	TELEM_DISABLE(telem_ctrl);
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				      0, 0, &telem_ctrl, NULL);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
>>   		return ret;
>> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   		/* Clear All Events */
>>   		TELEM_CLEAR_EVENTS(telem_ctrl);
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			return ret;
>> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   		/* Clear All Events */
>>   		TELEM_CLEAR_EVENTS(telem_ctrl);
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			return ret;
>> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
>> telemetry_evtconfig evtconfig,
>>   	TELEM_ENABLE_PERIODIC(telem_ctrl);
>>   	telem_ctrl |= pss_period;
>>
>> -	ret =
>> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				      0, 0, &telem_ctrl, NULL);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
>>   	if (ret) {
>>   		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
>>   		return ret;
>> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
>> *pdev)  {
>>   	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
>>   	u32 read_buf, events, event_regs;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>   	int ret;
>>
>>   	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
>> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
>> telemetry_setup(struct platform_device *pdev)
>>   	telm_conf->ioss_config.max_period =
>> TELEM_MAX_PERIOD(read_buf);
>>
>>   	/* PUNIT Mailbox Setup */
>> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
>> 0, 0,
>> -				      NULL, &read_buf);
>> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
>> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +			NULL, 0, &read_buf, 1, 0, 0);
>>   	if (ret) {
>>   		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
>>   		return ret;
>> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)  {
>>   	u32 telem_ctrl = 0;
>>   	int ret = 0;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	mutex_lock(&(telm_conf->telem_lock));
>>   	if (ioss_period) {
>> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>   		}
>>
>>   		/* Get telemetry EVENT CTL */
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
>> -				0, 0, NULL, &telem_ctrl);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &telem_ctrl, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Read Failed\n");
>>   			goto out;
>> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>
>>   		/* Disable Telemetry */
>>   		TELEM_DISABLE(telem_ctrl);
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> +				0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Disable Write
>> Failed\n");
>>   			goto out;
>> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
>> pss_period, u8 ioss_period)
>>   		TELEM_ENABLE_PERIODIC(telem_ctrl);
>>   		telem_ctrl |= pss_period;
>>
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> -				0, 0, &telem_ctrl, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
>> +				0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
>> +				0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TELEM_CTRL Event Enable Write
>> Failed\n");
>>   			goto out;
>> @@ -1013,6 +1025,7 @@ static int telemetry_plt_get_trace_verbosity(enum
>> telemetry_unit telem_unit,  {
>>   	u32 temp = 0;
>>   	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	if (verbosity == NULL)
>>   		return -EINVAL;
>> @@ -1020,9 +1033,9 @@ static int telemetry_plt_get_trace_verbosity(enum
>> telemetry_unit telem_unit,
>>   	mutex_lock(&(telm_conf->telem_trace_lock));
>>   	switch (telem_unit) {
>>   	case TELEM_PSS:
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
>> -				0, 0, NULL, &temp);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &temp, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Read Failed\n");
>>   			goto out;
>> @@ -1058,15 +1071,16 @@ static int
>> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
>>   	u32 temp = 0;
>>   	int ret;
>> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
>>
>>   	verbosity &= TELEM_TRC_VERBOSITY_MASK;
>>
>>   	mutex_lock(&(telm_conf->telem_trace_lock));
>>   	switch (telem_unit) {
>>   	case TELEM_PSS:
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
>> -				0, 0, NULL, &temp);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				NULL, 0, &temp, 1, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Read Failed\n");
>>   			goto out;
>> @@ -1074,10 +1088,10 @@ static int
>> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
>>
>>   		TELEM_CLEAR_VERBOSITY_BITS(temp);
>>   		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
>> -
>> -		ret = intel_punit_ipc_command(
>> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
>> -				0, 0, &temp, NULL);
>> +		punit_cmd_init(cmd,
>> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
>> +				0, 0);
>> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
>> PUNIT_PARAM_LEN,
>> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
>>   		if (ret) {
>>   			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
>>   			goto out;
>> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
>> platform_device *pdev)
>>   	if (!id)
>>   		return -ENODEV;
>>
>> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
>> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
>> +		return PTR_ERR(punit_bios_ipc_dev);
>> +
>>   	telm_conf = (struct telemetry_plt_config *)id->driver_data;
>>
>>   	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
>> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct platform_device
>> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)  {
>>   	telemetry_clear_pltdata();
>> +	intel_ipc_dev_put(punit_bios_ipc_dev);
>>   	iounmap(telm_conf->pss_config.regmap);
>>   	iounmap(telm_conf->ioss_config.regmap);
>>
>> --
>> 2.7.4
>

-- 
Sathyanarayanan Kuppuswamy
Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
  2017-10-10 22:28       ` sathyanarayanan kuppuswamy
  (?)
@ 2017-10-11  3:32         ` Chakravarty, Souvik K
  -1 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:32 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid



> -----Original Message-----
> From: platform-driver-x86-owner@vger.kernel.org [mailto:platform-driver-
> x86-owner@vger.kernel.org] On Behalf Of sathyanarayanan kuppuswamy
> Sent: Wednesday, October 11, 2017 3:59 AM
> To: Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>;
> a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com
> Subject: Re: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
> device calls
> 
> 
> 
> On 10/08/2017 10:07 PM, Chakravarty, Souvik K wrote:
> >> From: sathyanarayanan.kuppuswamy@linux.intel.com
> >> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> >> Sent: Sunday, October 8, 2017 3:50 AM
> >> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> >> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> >> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> >> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org;
> >> Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>
> >> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> >> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> >> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic
> >> intel ipc device calls
> >>
> >> From: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >>
> >> Removed redundant IPC helper functions and refactored the driver to
> >> use APIs provided by generic IPC driver. This patch also cleans-up
> >> PUNIT IPC user
> >> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC
> driver.
> >>
> >> Signed-off-by: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> ---
> >>   arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
> >>   drivers/platform/x86/Kconfig                  |   1 +
> >>   drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
> >>   drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
> >>   4 files changed, 223 insertions(+), 303 deletions(-)
> >>
> >> Changes since v4:
> >>   * None
> >>
> >> Changes since v2:
> >>   * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
> >>   * Added intel_ipc_dev_put() support.
> >>
> >> Changes since v1:
> >>   * Removed custom APIs.
> >>   * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
> >>     IPC driver.
> >>
> >> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
> >> b/arch/x86/include/asm/intel_punit_ipc.h
> >> index 201eb9d..cf1630c 100644
> >> --- a/arch/x86/include/asm/intel_punit_ipc.h
> >> +++ b/arch/x86/include/asm/intel_punit_ipc.h
> >> @@ -1,10 +1,8 @@
> >>   #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
> >>   #define  _ASM_X86_INTEL_PUNIT_IPC_H_
> >>
> >> -/*
> >> - * Three types of 8bit P-Unit IPC commands are supported,
> >> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
> >> - */
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +
> >>   typedef enum {
> >>   	BIOS_IPC = 0,
> >>   	GTDRIVER_IPC,
> >> @@ -12,61 +10,60 @@ typedef enum {
> >>   	RESERVED_IPC,
> >>   } IPC_TYPE;
> >>
> >> -#define IPC_TYPE_OFFSET			6
> >> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
> >> IPC_TYPE_OFFSET)
> >> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
> >> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
> >> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
> >> +#define PUNIT_PARAM_LEN				3
> >>
> >>   /* BIOS => Pcode commands */
> >> -#define IPC_PUNIT_BIOS_ZERO
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_BIOS_VR_INTERFACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_BIOS_READ_PCS
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_BIOS_WRITE_PCS
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x03)
> >> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
> >> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
> >> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
> >> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x07)
> >> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
> >> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
> >> -#define IPC_PUNIT_BIOS_RESERVED
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
> >> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
> >> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
> >> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
> >> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
> >> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
> >> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
> >> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
> >> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
> >> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
> >> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
> >> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
> >> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
> >> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
> >> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
> >> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
> >> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
> >> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
> >> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
> >> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
> >> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
> >> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
> >> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
> >> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
> >> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
> >> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
> >> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
> >> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
> >>
> >>   /* GT Driver => Pcode commands */
> >> -#define IPC_PUNIT_GTD_ZERO
> 	(IPC_PUNIT_GTD_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_GTD_CONFIG
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
> >> -#define IPC_PUNIT_GTD_GET_WM_VAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
> >> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
> >> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
> >> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
> >> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
> >> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
> >> +#define IPC_PUNIT_GTD_ZERO			(0x00)
> >> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
> >> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
> >> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
> >> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
> >> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
> >> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
> >> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
> >> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
> >> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
> >>
> >>   /* ISP Driver => Pcode commands */
> >> -#define IPC_PUNIT_ISPD_ZERO
> 	(IPC_PUNIT_ISPD_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_ISPD_CONFIG
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
> >> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
> >> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
> >> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
> >> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
> >> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
> >> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
> >> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
> >> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
> >>
> >>   /* Error codes */
> >>   #define IPC_PUNIT_ERR_SUCCESS			0
> >> @@ -77,25 +74,11 @@ typedef enum {
> >>   #define IPC_PUNIT_ERR_INVALID_VR_ID		5
> >>   #define IPC_PUNIT_ERR_VR_ERR			6
> >>
> >> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> >> -
> >> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
> >> -int
> >> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> >> *out);
> >> -
> >> -#else
> >> -
> >> -static inline int intel_punit_ipc_simple_command(int cmd,
> >> -						  int para1, int para2)
> >> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2,
> >> +u32
> >> +param3)
> >>   {
> >> -	return -ENODEV;
> >> +	cmd[0] = param1;
> >> +	cmd[1] = param2;
> >> +	cmd[2] = param3;
> >>   }
> >>
> >> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32
> para2,
> >> -					  u32 *in, u32 *out)
> >> -{
> >> -	return -ENODEV;
> >> -}
> >> -
> >> -#endif /* CONFIG_INTEL_PUNIT_IPC */
> >> -
> >>   #endif
> >> diff --git a/drivers/platform/x86/Kconfig
> >> b/drivers/platform/x86/Kconfig index 724ee696..9442c23 100644
> >> --- a/drivers/platform/x86/Kconfig
> >> +++ b/drivers/platform/x86/Kconfig
> >> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
> >>
> >>   config INTEL_PUNIT_IPC
> >>   	tristate "Intel P-Unit IPC Driver"
> >> +	select REGMAP_MMIO
> >>   	---help---
> >>   	  This driver provides support for Intel P-Unit Mailbox IPC
> >> mechanism,
> >>   	  which is used to bridge the communications between kernel and P-
> >> Unit.
> >> diff --git a/drivers/platform/x86/intel_punit_ipc.c
> >> b/drivers/platform/x86/intel_punit_ipc.c
> >> index b5b8901..f310a05 100644
> >> --- a/drivers/platform/x86/intel_punit_ipc.c
> >> +++ b/drivers/platform/x86/intel_punit_ipc.c
> >> @@ -18,18 +18,18 @@
> >>   #include <linux/device.h>
> >>   #include <linux/interrupt.h>
> >>   #include <linux/platform_device.h>
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +#include <linux/regmap.h>
> >>   #include <asm/intel_punit_ipc.h>
> >>
> >> -/* IPC Mailbox registers */
> >> -#define OFFSET_DATA_LOW		0x0
> >> -#define OFFSET_DATA_HIGH	0x4
> >>   /* bit field of interface register */
> >>   #define	CMD_RUN			BIT(31)
> >> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
> >> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
> >>   #define	CMD_PARA1_SHIFT		8
> >>   #define	CMD_PARA2_SHIFT		16
> >>
> >> -#define CMD_TIMEOUT_SECONDS	1
> >> +/* IPC PUNIT commands */
> >> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
> >> 0)
> >>
> >>   enum {
> >>   	BASE_DATA = 0,
> >> @@ -39,187 +39,42 @@ enum {
> >>
> >>   typedef struct {
> >>   	struct device *dev;
> >> -	struct mutex lock;
> >> -	int irq;
> >> -	struct completion cmd_complete;
> >>   	/* base of interface and data registers */
> >>   	void __iomem *base[RESERVED_IPC][BASE_MAX];
> >> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
> >>   	IPC_TYPE type;
> >>   } IPC_DEV;
> >>
> >>   static IPC_DEV *punit_ipcdev;
> >>
> >> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_IFACE]);
> >> -}
> >> -
> >> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32
> >> cmd) - {
> >> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
> >> -}
> >> -
> >> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> >> -}
> >> -
> >> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> >> -}
> >> -
> >> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE
> >> type, u32
> >> data) -{
> >> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> >> -}
> >> -
> >> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE
> >> type, u32
> >> data) -{
> >> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> >> -}
> >> +const char *ipc_dev_name[RESERVED_IPC] = {
> >> +	PUNIT_BIOS_IPC_DEV,
> >> +	PUNIT_GTD_IPC_DEV,
> >> +	PUNIT_ISP_IPC_DEV
> >> +};
> >>
> >> -static const char *ipc_err_string(int error) -{
> >> -	if (error == IPC_PUNIT_ERR_SUCCESS)
> >> -		return "no error";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
> >> -		return "invalid command";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
> >> -		return "invalid parameter";
> >> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
> >> -		return "command timeout";
> >> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
> >> -		return "command locked";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
> >> -		return "invalid vr id";
> >> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
> >> -		return "vr error";
> >> -	else
> >> -		return "unknown error";
> >> -}
> >> +static struct regmap_config punit_regmap_config = {
> >> +        .reg_bits = 32,
> >> +        .reg_stride = 4,
> >> +        .val_bits = 32,
> >> +};
> >>
> >> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE
> >> type)
> >> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
> >>   {
> >> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> >> -	int errcode;
> >> -	int status;
> >> -
> >> -	if (ipcdev->irq) {
> >> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
> >> -						 CMD_TIMEOUT_SECONDS *
> >> HZ)) {
> >> -			dev_err(ipcdev->dev, "IPC timed out\n");
> >> -			return -ETIMEDOUT;
> >> -		}
> >> -	} else {
> >> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
> >> loops)
> >> -			udelay(1);
> >> -		if (!loops) {
> >> -			dev_err(ipcdev->dev, "IPC timed out\n");
> >> -			return -ETIMEDOUT;
> >> -		}
> >> -	}
> >> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
> >> +		return -EINVAL;
> >>
> >> -	status = ipc_read_status(ipcdev, type);
> >> -	errcode = status & CMD_ERRCODE_MASK;
> >> -	if (errcode) {
> >> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
> >> -			ipc_err_string(errcode), status);
> >> -		return -EIO;
> >> -	}
> >> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
> >> +		cmd_list[2] << CMD_PARA1_SHIFT;
> >>
> >>   	return 0;
> >>   }
> >>
> >> -/**
> >> - * intel_punit_ipc_simple_command() - Simple IPC command
> >> - * @cmd:	IPC command code.
> >> - * @para1:	First 8bit parameter, set 0 if not used.
> >> - * @para2:	Second 8bit parameter, set 0 if not used.
> >> - *
> >> - * Send a IPC command to P-Unit when there is no data transaction
> >> - *
> >> - * Return:	IPC error code or 0 on success.
> >> - */
> >> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
> >> -	IPC_DEV *ipcdev = punit_ipcdev;
> >> -	IPC_TYPE type;
> >> -	u32 val;
> >> -	int ret;
> >> -
> >> -	mutex_lock(&ipcdev->lock);
> >> -
> >> -	reinit_completion(&ipcdev->cmd_complete);
> >> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> >> -
> >> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> >> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> >> CMD_PARA1_SHIFT;
> >> -	ipc_write_cmd(ipcdev, type, val);
> >> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> >> -
> >> -	mutex_unlock(&ipcdev->lock);
> >> -
> >> -	return ret;
> >> -}
> >> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> >> -
> >> -/**
> >> - * intel_punit_ipc_command() - IPC command with data and pointers
> >> - * @cmd:	IPC command code.
> >> - * @para1:	First 8bit parameter, set 0 if not used.
> >> - * @para2:	Second 8bit parameter, set 0 if not used.
> >> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
> >> and ISPD.
> >> - * @out:	Output data.
> >> - *
> >> - * Send a IPC command to P-Unit with data transaction
> >> - *
> >> - * Return:	IPC error code or 0 on success.
> >> - */
> >> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in,
> >> u32
> >> *out) -{
> >> -	IPC_DEV *ipcdev = punit_ipcdev;
> >> -	IPC_TYPE type;
> >> -	u32 val;
> >> -	int ret;
> >> -
> >> -	mutex_lock(&ipcdev->lock);
> >> -
> >> -	reinit_completion(&ipcdev->cmd_complete);
> >> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> >> -
> >> -	if (in) {
> >> -		ipc_write_data_low(ipcdev, type, *in);
> >> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> >> -			ipc_write_data_high(ipcdev, type, *++in);
> >> -	}
> >> -
> >> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> >> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> >> CMD_PARA1_SHIFT;
> >> -	ipc_write_cmd(ipcdev, type, val);
> >> -
> >> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> >> -	if (ret)
> >> -		goto out;
> >> -
> >> -	if (out) {
> >> -		*out = ipc_read_data_low(ipcdev, type);
> >> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> >> -			*++out = ipc_read_data_high(ipcdev, type);
> >> -	}
> >> -
> >> -out:
> >> -	mutex_unlock(&ipcdev->lock);
> >> -	return ret;
> >> -}
> >> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> >> -
> >> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> >> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */
> >> +int
> >> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
> >> +		u32 outlen, u32 dptr, u32 sptr)
> >>   {
> >> -	IPC_DEV *ipcdev = dev_id;
> >> -
> >> -	complete(&ipcdev->cmd_complete);
> >> -	return IRQ_HANDLED;
> >> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
> >>   }
> >>
> >>   static int intel_punit_get_bars(struct platform_device *pdev) @@
> >> -282,9
> >> +137,77 @@ static int intel_punit_get_bars(struct platform_device
> >> +*pdev)
> >>   	return 0;
> >>   }
> >>
> >> +static int punit_ipc_err_code(int status) {
> >> +	return (status & CMD_ERRCODE_MASK); }
> >> +
> >> +static int punit_ipc_busy_check(int status) {
> >> +	return status | CMD_RUN;
> >> +}
> >> +
> >> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device
> *dev,
> >> +		const char *devname,
> >> +		int irq,
> >> +		void __iomem *base,
> >> +		void __iomem *data)
> >> +{
> >> +	struct intel_ipc_dev_ops *ops;
> >> +	struct intel_ipc_dev_cfg *cfg;
> >> +	struct regmap *cmd_regs, *data_regs;
> >> +
> >> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
> >> +        if (!cfg)
> >> +                return ERR_PTR(-ENOMEM);
> >> +
> >> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> >> +	if (!ops)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> >> "%s_%s",
> >> +						  devname, "base");
> >> +
> >> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
> >> +			&punit_regmap_config);
> >> +	if (IS_ERR(cmd_regs)) {
> >> +                dev_err(dev, "cmd_regs regmap init failed\n");
> >> +                return ERR_CAST(cmd_regs);;
> >> +        }
> >> +
> >> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> >> "%s_%s",
> >> +						  devname, "data");
> >> +
> >> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
> >> +			&punit_regmap_config);
> >> +        if (IS_ERR(data_regs)) {
> >> +                dev_err(dev, "data_regs regmap init failed\n");
> >> +                return ERR_CAST(data_regs);;
> >> +        }
> >> +
> >> +	/* set IPC dev ops */
> >> +	ops->to_err_code = punit_ipc_err_code;
> >> +	ops->busy_check = punit_ipc_busy_check;
> >> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> >> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> >> +
> >> +	if (irq > 0)
> >> +	        cfg->mode = IPC_DEV_MODE_IRQ;
> >> +	else
> >> +	        cfg->mode = IPC_DEV_MODE_POLLING;
> >> +
> >> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
> >> +	cfg->irq = irq;
> >> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
> >> +	cfg->cmd_regs = cmd_regs;
> >> +	cfg->data_regs = data_regs;
> >> +
> >> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
> >> +
> >>   static int intel_punit_ipc_probe(struct platform_device *pdev)  {
> >> -	int irq, ret;
> >> +	int irq, ret, i;
> >>
> >>   	punit_ipcdev = devm_kzalloc(&pdev->dev,
> >>   				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
> >> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct
> >> platform_device
> >> *pdev)
> >>   	platform_set_drvdata(pdev, punit_ipcdev);
> >>
> >>   	irq = platform_get_irq(pdev, 0);
> >> -	if (irq < 0) {
> >> -		punit_ipcdev->irq = 0;
> >> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
> >> -	} else {
> >> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
> >> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
> >> -				       &punit_ipcdev);
> >> -		if (ret) {
> >> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
> >> irq);
> >> -			return ret;
> >> -		}
> >> -		punit_ipcdev->irq = irq;
> >> -	}
> >>
> >>   	ret = intel_punit_get_bars(pdev);
> >>   	if (ret)
> >> -		goto out;
> >> +		return ret;
> >> +
> >> +	for (i = 0; i < RESERVED_IPC; i++) {
> >> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
> >> +				&pdev->dev,
> >> +				ipc_dev_name[i],
> >> +				irq,
> >> +				punit_ipcdev->base[i][BASE_IFACE],
> >> +				punit_ipcdev->base[i][BASE_DATA]);
> >> +
> >> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
> >> +			dev_err(&pdev->dev, "%s create failed\n",
> >> +					ipc_dev_name[i]);
> >> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
> >> +		}
> >> +	}
> >>
> >>   	punit_ipcdev->dev = &pdev->dev;
> >> -	mutex_init(&punit_ipcdev->lock);
> >> -	init_completion(&punit_ipcdev->cmd_complete);
> >>
> >> -out:
> >>   	return ret;
> >> -}
> >>
> >> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
> >> -	return 0;
> >>   }
> >>
> >>   static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@
> >> -332,7 +250,6 @@ static const struct acpi_device_id
> >> punit_ipc_acpi_ids[] = {
> >>
> >>   static struct platform_driver intel_punit_ipc_driver = {
> >>   	.probe = intel_punit_ipc_probe,
> >> -	.remove = intel_punit_ipc_remove,
> >>   	.driver = {
> >>   		.name = "intel_punit_ipc",
> >>   		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
> >> a/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> index e0424d5..bf8284a 100644
> >> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> > This is a logical separation and so should be a different patch.
> But if we split it into two different patches then it will break the bisect-
> ability of these patches.
> >
> >> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
> >>
> >>   static struct telemetry_plt_config *telm_conf;
> >> +static struct intel_ipc_dev *punit_bios_ipc_dev;
> > Simply punit_ipc_dev is good enough.
> There are three PUNIT IPC devices. So to differentiate between them I added
> extra string to it.

OK that makes sense. 
However punit_bios_ipc is neither the actual device type/name nor indicates the usage of it since BIOS has got nothing to do with it (except for exposing the SSRAM base addr).
Can we rename this to something that actually denotes what this is, e.g, "punit_telemetry_ipc_dev". Same change needs to be done in intel_punit_ipc.c as well. 

> >
> >>   /*
> >>    * The following counters are programmed by default during setup.
> >> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
> >>   	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
> >>   };
> >>
> >> -
> >>   static struct telemetry_evtmap
> >>
> >> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
> >> NTS] = {
> >>   	{"IA_CORE0_C6_RES",			0x0400},
> >> @@ -283,13 +283,12 @@ static inline int
> >> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline
> >> int
> >> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
> >>   	u32 write_buf;
> >> -	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	write_buf = evt_id | TELEM_EVENT_ENABLE;
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
> >> -				      index, 0, &write_buf, NULL);
> >> -
> >> -	return ret;
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> >> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
> >>   }
> > 1) Why use raw_cmd here? It should use ipc_dev_cmd() according to
> original design. Same everywhere though this file.
> In my initial versions of this patch set, I had only exported
> ipc_dev_raw_cmd() API. Since this driver refactoring work was done during
> that time, I have used intel_dev_raw_cmd() API in it. I have added
> ipc_dev_cmd() API only recently  to handle some new requirements in
> intel_scu_ipc.c driver.
> 
> I will fix it in next version.
> 
> > 2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time
> > thoughout the file. They can be made into a separate local API inside
> > telemetry, like telemetry_punit_send_cmd(). Same thoughout
> I will make this change in next version.
> >
> >>   static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
> >> evtconfig, @@ -435,6 +434,7 @@ static int
> >> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
> >>   	int ret, index, idx;
> >>   	u32 *pss_evtmap;
> >>   	u32 telem_ctrl;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	num_pss_evts = evtconfig.num_evts;
> >>   	pss_period = evtconfig.period;
> >> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>
> >>   	/* PSS Config */
> >>   	/* Get telemetry EVENT CTL */
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> >> -				      0, 0, NULL, &telem_ctrl);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN, NULL,
> >> +			0, &telem_ctrl, 1, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Read Failed\n");
> >>   		return ret;
> >> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>
> >>   	/* Disable Telemetry */
> >>   	TELEM_DISABLE(telem_ctrl);
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				      0, 0, &telem_ctrl, NULL);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
> >>   		return ret;
> >> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   		/* Clear All Events */
> >>   		TELEM_CLEAR_EVENTS(telem_ctrl);
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			return ret;
> >> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   		/* Clear All Events */
> >>   		TELEM_CLEAR_EVENTS(telem_ctrl);
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			return ret;
> >> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   	TELEM_ENABLE_PERIODIC(telem_ctrl);
> >>   	telem_ctrl |= pss_period;
> >>
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				      0, 0, &telem_ctrl, NULL);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
> >>   		return ret;
> >> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
> >> *pdev)  {
> >>   	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
> >>   	u32 read_buf, events, event_regs;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>   	int ret;
> >>
> >>   	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> >> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
> >> telemetry_setup(struct platform_device *pdev)
> >>   	telm_conf->ioss_config.max_period =
> TELEM_MAX_PERIOD(read_buf);
> >>
> >>   	/* PUNIT Mailbox Setup */
> >> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
> >> 0, 0,
> >> -				      NULL, &read_buf);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			NULL, 0, &read_buf, 1, 0, 0);
> >>   	if (ret) {
> >>   		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
> >>   		return ret;
> >> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)  {
> >>   	u32 telem_ctrl = 0;
> >>   	int ret = 0;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	mutex_lock(&(telm_conf->telem_lock));
> >>   	if (ioss_period) {
> >> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>   		}
> >>
> >>   		/* Get telemetry EVENT CTL */
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> >> -				0, 0, NULL, &telem_ctrl);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &telem_ctrl, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>
> >>   		/* Disable Telemetry */
> >>   		TELEM_DISABLE(telem_ctrl);
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> +				0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			goto out;
> >> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>   		TELEM_ENABLE_PERIODIC(telem_ctrl);
> >>   		telem_ctrl |= pss_period;
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> +				0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Enable Write
> Failed\n");
> >>   			goto out;
> >> @@ -1013,6 +1025,7 @@ static int
> >> telemetry_plt_get_trace_verbosity(enum
> >> telemetry_unit telem_unit,  {
> >>   	u32 temp = 0;
> >>   	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	if (verbosity == NULL)
> >>   		return -EINVAL;
> >> @@ -1020,9 +1033,9 @@ static int
> >> telemetry_plt_get_trace_verbosity(enum
> >> telemetry_unit telem_unit,
> >>   	mutex_lock(&(telm_conf->telem_trace_lock));
> >>   	switch (telem_unit) {
> >>   	case TELEM_PSS:
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> >> -				0, 0, NULL, &temp);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &temp, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -1058,15 +1071,16 @@ static int
> >> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
> >>   	u32 temp = 0;
> >>   	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	verbosity &= TELEM_TRC_VERBOSITY_MASK;
> >>
> >>   	mutex_lock(&(telm_conf->telem_trace_lock));
> >>   	switch (telem_unit) {
> >>   	case TELEM_PSS:
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> >> -				0, 0, NULL, &temp);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &temp, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -1074,10 +1088,10 @@ static int
> >> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> >>
> >>   		TELEM_CLEAR_VERBOSITY_BITS(temp);
> >>   		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> >> -
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
> >> -				0, 0, &temp, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> +				0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
> >>   			goto out;
> >> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
> >> platform_device *pdev)
> >>   	if (!id)
> >>   		return -ENODEV;
> >>
> >> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
> >> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
> >> +		return PTR_ERR(punit_bios_ipc_dev);
> >> +
> >>   	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> >>
> >>   	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> >> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct
> >> platform_device
> >> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)
> {
> >>   	telemetry_clear_pltdata();
> >> +	intel_ipc_dev_put(punit_bios_ipc_dev);
> >>   	iounmap(telm_conf->pss_config.regmap);
> >>   	iounmap(telm_conf->ioss_config.regmap);
> >>
> >> --
> >> 2.7.4
> >
> 
> --
> Sathyanarayanan Kuppuswamy
> Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
@ 2017-10-11  3:32         ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:32 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogcGxhdGZvcm0tZHJpdmVy
LXg4Ni1vd25lckB2Z2VyLmtlcm5lbC5vcmcgW21haWx0bzpwbGF0Zm9ybS1kcml2ZXItDQo+IHg4
Ni1vd25lckB2Z2VyLmtlcm5lbC5vcmddIE9uIEJlaGFsZiBPZiBzYXRoeWFuYXJheWFuYW4ga3Vw
cHVzd2FteQ0KPiBTZW50OiBXZWRuZXNkYXksIE9jdG9iZXIgMTEsIDIwMTcgMzo1OSBBTQ0KPiBU
bzogQ2hha3JhdmFydHksIFNvdXZpayBLIDxzb3V2aWsuay5jaGFrcmF2YXJ0eUBpbnRlbC5jb20+
Ow0KPiBhLnp1bW1vQHRvd2VydGVjaC5pdDsgeDg2QGtlcm5lbC5vcmc7IHdpbUBpZ3VhbmEuYmU7
DQo+IG1pbmdvQHJlZGhhdC5jb207IGFsZXhhbmRyZS5iZWxsb25pQGZyZWUtZWxlY3Ryb25zLmNv
bTsgWmhhLCBRaXBlbmcNCj4gPHFpcGVuZy56aGFAaW50ZWwuY29tPjsgaHBhQHp5dG9yLmNvbTsg
ZHZoYXJ0QGluZnJhZGVhZC5vcmc7DQo+IHRnbHhAbGludXRyb25peC5kZTsgbGVlLmpvbmVzQGxp
bmFyby5vcmc7IGFuZHlAaW5mcmFkZWFkLm9yZw0KPiBDYzogbGludXgtcnRjQHZnZXIua2VybmVs
Lm9yZzsgbGludXgtd2F0Y2hkb2dAdmdlci5rZXJuZWwub3JnOyBsaW51eC0NCj4ga2VybmVsQHZn
ZXIua2VybmVsLm9yZzsgcGxhdGZvcm0tZHJpdmVyLXg4NkB2Z2VyLmtlcm5lbC5vcmc7DQo+IHNh
dGh5YW9zaWRAZ21haWwuY29tDQo+IFN1YmplY3Q6IFJlOiBbUkZDIHY1IDYvOF0gcGxhdGZvcm0v
eDg2OiBpbnRlbF9wdW5pdF9pcGM6IFVzZSBnZW5lcmljIGludGVsIGlwYw0KPiBkZXZpY2UgY2Fs
bHMNCj4gDQo+IA0KPiANCj4gT24gMTAvMDgvMjAxNyAxMDowNyBQTSwgQ2hha3JhdmFydHksIFNv
dXZpayBLIHdyb3RlOg0KPiA+PiBGcm9tOiBzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2FteUBsaW51
eC5pbnRlbC5jb20NCj4gPj4gW21haWx0bzpzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2FteUBsaW51
eC5pbnRlbC5jb21dDQo+ID4+IFNlbnQ6IFN1bmRheSwgT2N0b2JlciA4LCAyMDE3IDM6NTAgQU0N
Cj4gPj4gVG86IGEuenVtbW9AdG93ZXJ0ZWNoLml0OyB4ODZAa2VybmVsLm9yZzsgd2ltQGlndWFu
YS5iZTsNCj4gPj4gbWluZ29AcmVkaGF0LmNvbTsgYWxleGFuZHJlLmJlbGxvbmlAZnJlZS1lbGVj
dHJvbnMuY29tOyBaaGEsIFFpcGVuZw0KPiA+PiA8cWlwZW5nLnpoYUBpbnRlbC5jb20+OyBocGFA
enl0b3IuY29tOyBkdmhhcnRAaW5mcmFkZWFkLm9yZzsNCj4gPj4gdGdseEBsaW51dHJvbml4LmRl
OyBsZWUuam9uZXNAbGluYXJvLm9yZzsgYW5keUBpbmZyYWRlYWQub3JnOw0KPiA+PiBDaGFrcmF2
YXJ0eSwgU291dmlrIEsgPHNvdXZpay5rLmNoYWtyYXZhcnR5QGludGVsLmNvbT4NCj4gPj4gQ2M6
IGxpbnV4LXJ0Y0B2Z2VyLmtlcm5lbC5vcmc7IGxpbnV4LXdhdGNoZG9nQHZnZXIua2VybmVsLm9y
ZzsgbGludXgtDQo+ID4+IGtlcm5lbEB2Z2VyLmtlcm5lbC5vcmc7IHBsYXRmb3JtLWRyaXZlci14
ODZAdmdlci5rZXJuZWwub3JnOw0KPiA+PiBzYXRoeWFvc2lkQGdtYWlsLmNvbTsgS3VwcHVzd2Ft
eSBTYXRoeWFuYXJheWFuYW4NCj4gPj4gPHNhdGh5YW5hcmF5YW5hbi5rdXBwdXN3YW15QGxpbnV4
LmludGVsLmNvbT4NCj4gPj4gU3ViamVjdDogW1JGQyB2NSA2LzhdIHBsYXRmb3JtL3g4NjogaW50
ZWxfcHVuaXRfaXBjOiBVc2UgZ2VuZXJpYw0KPiA+PiBpbnRlbCBpcGMgZGV2aWNlIGNhbGxzDQo+
ID4+DQo+ID4+IEZyb206IEt1cHB1c3dhbXkgU2F0aHlhbmFyYXlhbmFuDQo+ID4+IDxzYXRoeWFu
YXJheWFuYW4ua3VwcHVzd2FteUBsaW51eC5pbnRlbC5jb20+DQo+ID4+DQo+ID4+IFJlbW92ZWQg
cmVkdW5kYW50IElQQyBoZWxwZXIgZnVuY3Rpb25zIGFuZCByZWZhY3RvcmVkIHRoZSBkcml2ZXIg
dG8NCj4gPj4gdXNlIEFQSXMgcHJvdmlkZWQgYnkgZ2VuZXJpYyBJUEMgZHJpdmVyLiBUaGlzIHBh
dGNoIGFsc28gY2xlYW5zLXVwDQo+ID4+IFBVTklUIElQQyB1c2VyDQo+ID4+IGRyaXZlcnMoaW50
ZWxfdGVsZW1ldHJ5X3BsdGRydi5jKSB0byB1c2UgQVBJcyBwcm92aWRlZCBieSBnZW5lcmljIElQ
Qw0KPiBkcml2ZXIuDQo+ID4+DQo+ID4+IFNpZ25lZC1vZmYtYnk6IEt1cHB1c3dhbXkgU2F0aHlh
bmFyYXlhbmFuDQo+ID4+IDxzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2FteUBsaW51eC5pbnRlbC5j
b20+DQo+ID4+IC0tLQ0KPiA+PiAgIGFyY2gveDg2L2luY2x1ZGUvYXNtL2ludGVsX3B1bml0X2lw
Yy5oICAgICAgICB8IDEyNSArKysrKy0tLS0tLQ0KPiA+PiAgIGRyaXZlcnMvcGxhdGZvcm0veDg2
L0tjb25maWcgICAgICAgICAgICAgICAgICB8ICAgMSArDQo+ID4+ICAgZHJpdmVycy9wbGF0Zm9y
bS94ODYvaW50ZWxfcHVuaXRfaXBjLmMgICAgICAgIHwgMzAzICsrKysrKysrKystLS0tLS0tLS0t
LS0tLS0tDQo+ID4+ICAgZHJpdmVycy9wbGF0Zm9ybS94ODYvaW50ZWxfdGVsZW1ldHJ5X3BsdGRy
di5jIHwgIDk3ICsrKysrLS0tLQ0KPiA+PiAgIDQgZmlsZXMgY2hhbmdlZCwgMjIzIGluc2VydGlv
bnMoKyksIDMwMyBkZWxldGlvbnMoLSkNCj4gPj4NCj4gPj4gQ2hhbmdlcyBzaW5jZSB2NDoNCj4g
Pj4gICAqIE5vbmUNCj4gPj4NCj4gPj4gQ2hhbmdlcyBzaW5jZSB2MjoNCj4gPj4gICAqIEFkZGVk
IHVuaXF1ZSBuYW1lIHRvIFBVTklUIEJJT1MsIEdURCwgJiBJU1AgcmVnbWFwcy4NCj4gPj4gICAq
IEFkZGVkIGludGVsX2lwY19kZXZfcHV0KCkgc3VwcG9ydC4NCj4gPj4NCj4gPj4gQ2hhbmdlcyBz
aW5jZSB2MToNCj4gPj4gICAqIFJlbW92ZWQgY3VzdG9tIEFQSXMuDQo+ID4+ICAgKiBDbGVhbmVk
IHVwIFBVTklUIElQQyB1c2VyIGRyaXZlcnMgdG8gdXNlIEFQSXMgcHJvdmlkZWQgYnkgZ2VuZXJp
Yw0KPiA+PiAgICAgSVBDIGRyaXZlci4NCj4gPj4NCj4gPj4gZGlmZiAtLWdpdCBhL2FyY2gveDg2
L2luY2x1ZGUvYXNtL2ludGVsX3B1bml0X2lwYy5oDQo+ID4+IGIvYXJjaC94ODYvaW5jbHVkZS9h
c20vaW50ZWxfcHVuaXRfaXBjLmgNCj4gPj4gaW5kZXggMjAxZWI5ZC4uY2YxNjMwYyAxMDA2NDQN
Cj4gPj4gLS0tIGEvYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxfcHVuaXRfaXBjLmgNCj4gPj4g
KysrIGIvYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxfcHVuaXRfaXBjLmgNCj4gPj4gQEAgLTEs
MTAgKzEsOCBAQA0KPiA+PiAgICNpZm5kZWYgX0FTTV9YODZfSU5URUxfUFVOSVRfSVBDX0hfDQo+
ID4+ICAgI2RlZmluZSAgX0FTTV9YODZfSU5URUxfUFVOSVRfSVBDX0hfDQo+ID4+DQo+ID4+IC0v
Kg0KPiA+PiAtICogVGhyZWUgdHlwZXMgb2YgOGJpdCBQLVVuaXQgSVBDIGNvbW1hbmRzIGFyZSBz
dXBwb3J0ZWQsDQo+ID4+IC0gKiBiaXRbNzo2XTogWzAwXTogQklPUzsgWzAxXTogR1REOyBbMTBd
OiBJU1BELg0KPiA+PiAtICovDQo+ID4+ICsjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGF0YS94
ODYvaW50ZWxfaXBjX2Rldi5oPg0KPiA+PiArDQo+ID4+ICAgdHlwZWRlZiBlbnVtIHsNCj4gPj4g
ICAJQklPU19JUEMgPSAwLA0KPiA+PiAgIAlHVERSSVZFUl9JUEMsDQo+ID4+IEBAIC0xMiw2MSAr
MTAsNjAgQEAgdHlwZWRlZiBlbnVtIHsNCj4gPj4gICAJUkVTRVJWRURfSVBDLA0KPiA+PiAgIH0g
SVBDX1RZUEU7DQo+ID4+DQo+ID4+IC0jZGVmaW5lIElQQ19UWVBFX09GRlNFVAkJCTYNCj4gPj4g
LSNkZWZpbmUgSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UJCShCSU9TX0lQQyA8PA0KPiA+PiBJUENf
VFlQRV9PRkZTRVQpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9HVERfQ01EX0JBU0UJCShHVERE
UklWRVJfSVBDIDw8DQo+ID4+IElQQ19UWVBFX09GRlNFVCkNCj4gPj4gLSNkZWZpbmUgSVBDX1BV
TklUX0lTUERfQ01EX0JBU0UJCShJU1BEUklWRVJfSVBDIDw8DQo+ID4+IElQQ19UWVBFX09GRlNF
VCkNCj4gPj4gLSNkZWZpbmUgSVBDX1BVTklUX0NNRF9UWVBFX01BU0sJCShSRVNFUlZFRF9JUEMg
PDwNCj4gPj4gSVBDX1RZUEVfT0ZGU0VUKQ0KPiA+PiArI2RlZmluZSBQVU5JVF9CSU9TX0lQQ19E
RVYJCQkicHVuaXRfYmlvc19pcGMiDQo+ID4+ICsjZGVmaW5lIFBVTklUX0dURF9JUENfREVWCQkJ
InB1bml0X2d0ZF9pcGMiDQo+ID4+ICsjZGVmaW5lIFBVTklUX0lTUF9JUENfREVWCQkJInB1bml0
X2lzcF9pcGMiDQo+ID4+ICsjZGVmaW5lIFBVTklUX1BBUkFNX0xFTgkJCQkzDQo+ID4+DQo+ID4+
ICAgLyogQklPUyA9PiBQY29kZSBjb21tYW5kcyAqLw0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRf
QklPU19aRVJPDQo+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UNCj4gPj4gfCAweDAwKQ0KPiA+
PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19WUl9JTlRFUkZBQ0UNCj4gPj4gCShJUENfUFVOSVRf
QklPU19DTURfQkFTRSB8IDB4MDEpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURf
UENTDQo+ID4+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UgfCAweDAyKQ0KPiA+PiAtI2RlZmlu
ZSBJUENfUFVOSVRfQklPU19XUklURV9QQ1MNCj4gCShJUENfUFVOSVRfQklPU19DTURfQkFTRQ0K
PiA+PiB8IDB4MDMpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURfUENVX0NPTkZJ
Rw0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgwNCkNCj4gPj4gLSNkZWZpbmUg
SVBDX1BVTklUX0JJT1NfV1JJVEVfUENVX0NPTkZJRw0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NN
RF9CQVNFIHwgMHgwNSkNCj4gPj4gLSNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9QTDFfU0VU
VElORw0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgwNikNCj4gPj4gLSNkZWZp
bmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfUEwxX1NFVFRJTkcNCj4gCShJUENfUFVOSVRfQklPU19D
TURfQkFTRQ0KPiA+PiB8IDB4MDcpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1RSSUdH
RVJfVkREX1JBTQ0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgwOCkNCj4gPj4g
LSNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0lORk8NCj4gPj4gCShJUENfUFVOSVRf
QklPU19DTURfQkFTRSB8IDB4MDkpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURf
VEVMRV9UUkFDRV9DVFJMDQo+ID4+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UgfCAweDBhKQ0K
PiA+PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklURV9URUxFX1RSQUNFX0NUUkwNCj4gPj4g
CShJUENfUFVOSVRfQklPU19DTURfQkFTRSB8IDB4MGIpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5J
VF9CSU9TX1JFQURfVEVMRV9FVkVOVF9DVFJMDQo+ID4+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JB
U0UgfCAweDBjKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklURV9URUxFX0VWRU5U
X0NUUkwNCj4gPj4gCShJUENfUFVOSVRfQklPU19DTURfQkFTRSB8IDB4MGQpDQo+ID4+IC0jZGVm
aW5lIElQQ19QVU5JVF9CSU9TX1JFQURfVEVMRV9UUkFDRQ0KPiA+PiAJKElQQ19QVU5JVF9CSU9T
X0NNRF9CQVNFIHwgMHgwZSkNCj4gPj4gLSNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfVEVM
RV9UUkFDRQ0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgwZikNCj4gPj4gLSNk
ZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0VWRU5UDQo+ID4+IAkoSVBDX1BVTklUX0JJ
T1NfQ01EX0JBU0UgfCAweDEwKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklURV9U
RUxFX0VWRU5UDQo+ID4+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UgfCAweDExKQ0KPiA+PiAt
I2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFEX01PRFVMRV9URU1QDQo+ID4+IAkoSVBDX1BVTklU
X0JJT1NfQ01EX0JBU0UgfCAweDEyKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19SRVNF
UlZFRA0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgxMykNCj4gPj4gLSNkZWZp
bmUgSVBDX1BVTklUX0JJT1NfUkVBRF9WT0xUQUdFX09WRVINCj4gPj4gCShJUENfUFVOSVRfQklP
U19DTURfQkFTRSB8IDB4MTQpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1dSSVRFX1ZP
TFRBR0VfT1ZFUg0KPiA+PiAJKElQQ19QVU5JVF9CSU9TX0NNRF9CQVNFIHwgMHgxNSkNCj4gPj4g
LSNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9SQVRJT19PVkVSDQo+ID4+IAkoSVBDX1BVTklU
X0JJT1NfQ01EX0JBU0UgfCAweDE2KQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklU
RV9SQVRJT19PVkVSDQo+ID4+IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UgfCAweDE3KQ0KPiA+
PiAtI2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFEX1ZGX0dMX0NUUkwNCj4gPj4gCShJUENfUFVO
SVRfQklPU19DTURfQkFTRSB8IDB4MTgpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1dS
SVRFX1ZGX0dMX0NUUkwNCj4gPj4gCShJUENfUFVOSVRfQklPU19DTURfQkFTRSB8IDB4MTkpDQo+
ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURfRk1fU09DX1RFTVBfVEhSRVNIDQo+ID4+
IAkoSVBDX1BVTklUX0JJT1NfQ01EX0JBU0UgfCAweDFhKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVO
SVRfQklPU19XUklURV9GTV9TT0NfVEVNUF9USFJFU0gNCj4gPj4gCShJUENfUFVOSVRfQklPU19D
TURfQkFTRSB8IDB4MWIpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5JVF9CSU9TX1pFUk8JCQkoMHgw
MCkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfVlJfSU5URVJGQUNFCQkoMHgwMSkNCj4g
Pj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9QQ1MJCQkoMHgwMikNCj4gPj4gKyNkZWZp
bmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfUENTCQkoMHgwMykNCj4gPj4gKyNkZWZpbmUgSVBDX1BV
TklUX0JJT1NfUkVBRF9QQ1VfQ09ORklHCQkoMHgwNCkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklU
X0JJT1NfV1JJVEVfUENVX0NPTkZJRwkJKDB4MDUpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5JVF9C
SU9TX1JFQURfUEwxX1NFVFRJTkcJCSgweDA2KQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklP
U19XUklURV9QTDFfU0VUVElORwkoMHgwNykNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1Nf
VFJJR0dFUl9WRERfUkFNCQkoMHgwOCkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVB
RF9URUxFX0lORk8JCSgweDA5KQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFEX1RF
TEVfVFJBQ0VfQ1RSTAkoMHgwYSkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJVEVf
VEVMRV9UUkFDRV9DVFJMCSgweDBiKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFE
X1RFTEVfRVZFTlRfQ1RSTAkoMHgwYykNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJ
VEVfVEVMRV9FVkVOVF9DVFJMCSgweDBkKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19S
RUFEX1RFTEVfVFJBQ0UJCSgweDBlKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklU
RV9URUxFX1RSQUNFCQkoMHgwZikNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9U
RUxFX0VWRU5UCQkoMHgxMCkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfVEVM
RV9FVkVOVAkJKDB4MTEpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURfTU9EVUxF
X1RFTVAJCSgweDEyKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19SRVNFUlZFRAkJCSgw
eDEzKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFEX1ZPTFRBR0VfT1ZFUgkoMHgx
NCkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfVk9MVEFHRV9PVkVSCSgweDE1
KQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19SRUFEX1JBVElPX09WRVIJCSgweDE2KQ0K
PiA+PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklURV9SQVRJT19PVkVSCQkoMHgxNykNCj4g
Pj4gKyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfUkVBRF9WRl9HTF9DVFJMCQkoMHgxOCkNCj4gPj4g
KyNkZWZpbmUgSVBDX1BVTklUX0JJT1NfV1JJVEVfVkZfR0xfQ1RSTAkJKDB4MTkpDQo+ID4+ICsj
ZGVmaW5lIElQQ19QVU5JVF9CSU9TX1JFQURfRk1fU09DX1RFTVBfVEhSRVNICSgweDFhKQ0KPiA+
PiArI2RlZmluZSBJUENfUFVOSVRfQklPU19XUklURV9GTV9TT0NfVEVNUF9USFJFU0gJKDB4MWIp
DQo+ID4+DQo+ID4+ICAgLyogR1QgRHJpdmVyID0+IFBjb2RlIGNvbW1hbmRzICovDQo+ID4+IC0j
ZGVmaW5lIElQQ19QVU5JVF9HVERfWkVSTw0KPiAJKElQQ19QVU5JVF9HVERfQ01EX0JBU0UNCj4g
Pj4gfCAweDAwKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfR1REX0NPTkZJRw0KPiA+PiAJKElQ
Q19QVU5JVF9HVERfQ01EX0JBU0UgfCAweDAxKQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfR1RE
X1JFQURfSUNDUF9MSUNfQ0RZTl9TQ0FMDQo+ID4+IAkoSVBDX1BVTklUX0dURF9DTURfQkFTRSB8
IDB4MDIpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9HVERfV1JJVEVfSUNDUF9MSUNfQ0RZTl9T
Q0FMDQo+ID4+IAkoSVBDX1BVTklUX0dURF9DTURfQkFTRSB8IDB4MDMpDQo+ID4+IC0jZGVmaW5l
IElQQ19QVU5JVF9HVERfR0VUX1dNX1ZBTA0KPiA+PiAJKElQQ19QVU5JVF9HVERfQ01EX0JBU0Ug
fCAweDA2KQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfR1REX1dSSVRFX0NPTkZJR19XSVNIUkVR
DQo+ID4+IAkoSVBDX1BVTklUX0dURF9DTURfQkFTRSB8IDB4MDcpDQo+ID4+IC0jZGVmaW5lIElQ
Q19QVU5JVF9HVERfUkVBRF9SRVFfRFVUWV9DWUNMRQ0KPiA+PiAJKElQQ19QVU5JVF9HVERfQ01E
X0JBU0UgfCAweDE2KQ0KPiA+PiAtI2RlZmluZSBJUENfUFVOSVRfR1REX0RJU19WT0xfRlJFUV9D
SEdfUkVRVUVTVA0KPiA+PiAJKElQQ19QVU5JVF9HVERfQ01EX0JBU0UgfCAweDE3KQ0KPiA+PiAt
I2RlZmluZSBJUENfUFVOSVRfR1REX0RZTkFfRFVUWV9DWUNMRV9DVFJMDQo+ID4+IAkoSVBDX1BV
TklUX0dURF9DTURfQkFTRSB8IDB4MWEpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9HVERfRFlO
QV9EVVRZX0NZQ0xFX1RVTklORw0KPiA+PiAJKElQQ19QVU5JVF9HVERfQ01EX0JBU0UgfCAweDFj
KQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfR1REX1pFUk8JCQkoMHgwMCkNCj4gPj4gKyNkZWZp
bmUgSVBDX1BVTklUX0dURF9DT05GSUcJCQkoMHgwMSkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklU
X0dURF9SRUFEX0lDQ1BfTElDX0NEWU5fU0NBTAkoMHgwMikNCj4gPj4gKyNkZWZpbmUgSVBDX1BV
TklUX0dURF9XUklURV9JQ0NQX0xJQ19DRFlOX1NDQUwJKDB4MDMpDQo+ID4+ICsjZGVmaW5lIElQ
Q19QVU5JVF9HVERfR0VUX1dNX1ZBTAkJKDB4MDYpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5JVF9H
VERfV1JJVEVfQ09ORklHX1dJU0hSRVEJKDB4MDcpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5JVF9H
VERfUkVBRF9SRVFfRFVUWV9DWUNMRQkoMHgxNikNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0dU
RF9ESVNfVk9MX0ZSRVFfQ0hHX1JFUVVFU1QJKDB4MTcpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5J
VF9HVERfRFlOQV9EVVRZX0NZQ0xFX0NUUkwJKDB4MWEpDQo+ID4+ICsjZGVmaW5lIElQQ19QVU5J
VF9HVERfRFlOQV9EVVRZX0NZQ0xFX1RVTklORwkoMHgxYykNCj4gPj4NCj4gPj4gICAvKiBJU1Ag
RHJpdmVyID0+IFBjb2RlIGNvbW1hbmRzICovDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9JU1BE
X1pFUk8NCj4gCShJUENfUFVOSVRfSVNQRF9DTURfQkFTRQ0KPiA+PiB8IDB4MDApDQo+ID4+IC0j
ZGVmaW5lIElQQ19QVU5JVF9JU1BEX0NPTkZJRw0KPiA+PiAJKElQQ19QVU5JVF9JU1BEX0NNRF9C
QVNFIHwgMHgwMSkNCj4gPj4gLSNkZWZpbmUgSVBDX1BVTklUX0lTUERfR0VUX0lTUF9MVFJfVkFM
DQo+ID4+IAkoSVBDX1BVTklUX0lTUERfQ01EX0JBU0UgfCAweDAyKQ0KPiA+PiAtI2RlZmluZSBJ
UENfUFVOSVRfSVNQRF9BQ0NFU1NfSVVfRlJFUV9CT1VORFMNCj4gPj4gCShJUENfUFVOSVRfSVNQ
RF9DTURfQkFTRSB8IDB4MDMpDQo+ID4+IC0jZGVmaW5lIElQQ19QVU5JVF9JU1BEX1JFQURfQ0RZ
Tl9MRVZFTA0KPiA+PiAJKElQQ19QVU5JVF9JU1BEX0NNRF9CQVNFIHwgMHgwNCkNCj4gPj4gLSNk
ZWZpbmUgSVBDX1BVTklUX0lTUERfV1JJVEVfQ0RZTl9MRVZFTA0KPiA+PiAJKElQQ19QVU5JVF9J
U1BEX0NNRF9CQVNFIHwgMHgwNSkNCj4gPj4gKyNkZWZpbmUgSVBDX1BVTklUX0lTUERfWkVSTwkJ
CSgweDAwKQ0KPiA+PiArI2RlZmluZSBJUENfUFVOSVRfSVNQRF9DT05GSUcJCQkoMHgwMSkNCj4g
Pj4gKyNkZWZpbmUgSVBDX1BVTklUX0lTUERfR0VUX0lTUF9MVFJfVkFMCQkoMHgwMikNCj4gPj4g
KyNkZWZpbmUgSVBDX1BVTklUX0lTUERfQUNDRVNTX0lVX0ZSRVFfQk9VTkRTCSgweDAzKQ0KPiA+
PiArI2RlZmluZSBJUENfUFVOSVRfSVNQRF9SRUFEX0NEWU5fTEVWRUwJCSgweDA0KQ0KPiA+PiAr
I2RlZmluZSBJUENfUFVOSVRfSVNQRF9XUklURV9DRFlOX0xFVkVMCQkoMHgwNSkNCj4gPj4NCj4g
Pj4gICAvKiBFcnJvciBjb2RlcyAqLw0KPiA+PiAgICNkZWZpbmUgSVBDX1BVTklUX0VSUl9TVUND
RVNTCQkJMA0KPiA+PiBAQCAtNzcsMjUgKzc0LDExIEBAIHR5cGVkZWYgZW51bSB7DQo+ID4+ICAg
I2RlZmluZSBJUENfUFVOSVRfRVJSX0lOVkFMSURfVlJfSUQJCTUNCj4gPj4gICAjZGVmaW5lIElQ
Q19QVU5JVF9FUlJfVlJfRVJSCQkJNg0KPiA+Pg0KPiA+PiAtI2lmIElTX0VOQUJMRUQoQ09ORklH
X0lOVEVMX1BVTklUX0lQQykNCj4gPj4gLQ0KPiA+PiAtaW50IGludGVsX3B1bml0X2lwY19zaW1w
bGVfY29tbWFuZChpbnQgY21kLCBpbnQgcGFyYTEsIGludCBwYXJhMik7DQo+ID4+IC1pbnQNCj4g
Pj4gaW50ZWxfcHVuaXRfaXBjX2NvbW1hbmQodTMyIGNtZCwgdTMyIHBhcmExLCB1MzIgcGFyYTIs
IHUzMiAqaW4sIHUzMg0KPiA+PiAqb3V0KTsNCj4gPj4gLQ0KPiA+PiAtI2Vsc2UNCj4gPj4gLQ0K
PiA+PiAtc3RhdGljIGlubGluZSBpbnQgaW50ZWxfcHVuaXRfaXBjX3NpbXBsZV9jb21tYW5kKGlu
dCBjbWQsDQo+ID4+IC0JCQkJCQkgIGludCBwYXJhMSwgaW50IHBhcmEyKQ0KPiA+PiArc3RhdGlj
IGlubGluZSB2b2lkIHB1bml0X2NtZF9pbml0KHUzMiAqY21kLCB1MzIgcGFyYW0xLCB1MzIgcGFy
YW0yLA0KPiA+PiArdTMyDQo+ID4+ICtwYXJhbTMpDQo+ID4+ICAgew0KPiA+PiAtCXJldHVybiAt
RU5PREVWOw0KPiA+PiArCWNtZFswXSA9IHBhcmFtMTsNCj4gPj4gKwljbWRbMV0gPSBwYXJhbTI7
DQo+ID4+ICsJY21kWzJdID0gcGFyYW0zOw0KPiA+PiAgIH0NCj4gPj4NCj4gPj4gLXN0YXRpYyBp
bmxpbmUgaW50IGludGVsX3B1bml0X2lwY19jb21tYW5kKHUzMiBjbWQsIHUzMiBwYXJhMSwgdTMy
DQo+IHBhcmEyLA0KPiA+PiAtCQkJCQkgIHUzMiAqaW4sIHUzMiAqb3V0KQ0KPiA+PiAtew0KPiA+
PiAtCXJldHVybiAtRU5PREVWOw0KPiA+PiAtfQ0KPiA+PiAtDQo+ID4+IC0jZW5kaWYgLyogQ09O
RklHX0lOVEVMX1BVTklUX0lQQyAqLw0KPiA+PiAtDQo+ID4+ICAgI2VuZGlmDQo+ID4+IGRpZmYg
LS1naXQgYS9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9LY29uZmlnDQo+ID4+IGIvZHJpdmVycy9wbGF0
Zm9ybS94ODYvS2NvbmZpZyBpbmRleCA3MjRlZTY5Ni4uOTQ0MmMyMyAxMDA2NDQNCj4gPj4gLS0t
IGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvS2NvbmZpZw0KPiA+PiArKysgYi9kcml2ZXJzL3BsYXRm
b3JtL3g4Ni9LY29uZmlnDQo+ID4+IEBAIC0xMDk2LDYgKzEwOTYsNyBAQCBjb25maWcgU1VSRkFD
RV8zX0JVVFRPTg0KPiA+Pg0KPiA+PiAgIGNvbmZpZyBJTlRFTF9QVU5JVF9JUEMNCj4gPj4gICAJ
dHJpc3RhdGUgIkludGVsIFAtVW5pdCBJUEMgRHJpdmVyIg0KPiA+PiArCXNlbGVjdCBSRUdNQVBf
TU1JTw0KPiA+PiAgIAktLS1oZWxwLS0tDQo+ID4+ICAgCSAgVGhpcyBkcml2ZXIgcHJvdmlkZXMg
c3VwcG9ydCBmb3IgSW50ZWwgUC1Vbml0IE1haWxib3ggSVBDDQo+ID4+IG1lY2hhbmlzbSwNCj4g
Pj4gICAJICB3aGljaCBpcyB1c2VkIHRvIGJyaWRnZSB0aGUgY29tbXVuaWNhdGlvbnMgYmV0d2Vl
biBrZXJuZWwgYW5kIFAtDQo+ID4+IFVuaXQuDQo+ID4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3Bs
YXRmb3JtL3g4Ni9pbnRlbF9wdW5pdF9pcGMuYw0KPiA+PiBiL2RyaXZlcnMvcGxhdGZvcm0veDg2
L2ludGVsX3B1bml0X2lwYy5jDQo+ID4+IGluZGV4IGI1Yjg5MDEuLmYzMTBhMDUgMTAwNjQ0DQo+
ID4+IC0tLSBhL2RyaXZlcnMvcGxhdGZvcm0veDg2L2ludGVsX3B1bml0X2lwYy5jDQo+ID4+ICsr
KyBiL2RyaXZlcnMvcGxhdGZvcm0veDg2L2ludGVsX3B1bml0X2lwYy5jDQo+ID4+IEBAIC0xOCwx
OCArMTgsMTggQEANCj4gPj4gICAjaW5jbHVkZSA8bGludXgvZGV2aWNlLmg+DQo+ID4+ICAgI2lu
Y2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPg0KPiA+PiAgICNpbmNsdWRlIDxsaW51eC9wbGF0Zm9y
bV9kZXZpY2UuaD4NCj4gPj4gKyNpbmNsdWRlIDxsaW51eC9wbGF0Zm9ybV9kYXRhL3g4Ni9pbnRl
bF9pcGNfZGV2Lmg+DQo+ID4+ICsjaW5jbHVkZSA8bGludXgvcmVnbWFwLmg+DQo+ID4+ICAgI2lu
Y2x1ZGUgPGFzbS9pbnRlbF9wdW5pdF9pcGMuaD4NCj4gPj4NCj4gPj4gLS8qIElQQyBNYWlsYm94
IHJlZ2lzdGVycyAqLw0KPiA+PiAtI2RlZmluZSBPRkZTRVRfREFUQV9MT1cJCTB4MA0KPiA+PiAt
I2RlZmluZSBPRkZTRVRfREFUQV9ISUdICTB4NA0KPiA+PiAgIC8qIGJpdCBmaWVsZCBvZiBpbnRl
cmZhY2UgcmVnaXN0ZXIgKi8NCj4gPj4gICAjZGVmaW5lCUNNRF9SVU4JCQlCSVQoMzEpDQo+ID4+
IC0jZGVmaW5lCUNNRF9FUlJDT0RFX01BU0sJR0VOTUFTSyg3LCAwKQ0KPiA+PiArI2RlZmluZSBD
TURfRVJSQ09ERV9NQVNLCUdFTk1BU0soNywgMCkNCj4gPj4gICAjZGVmaW5lCUNNRF9QQVJBMV9T
SElGVAkJOA0KPiA+PiAgICNkZWZpbmUJQ01EX1BBUkEyX1NISUZUCQkxNg0KPiA+Pg0KPiA+PiAt
I2RlZmluZSBDTURfVElNRU9VVF9TRUNPTkRTCTENCj4gPj4gKy8qIElQQyBQVU5JVCBjb21tYW5k
cyAqLw0KPiA+PiArI2RlZmluZQlJUENfREVWX1BVTklUX0NNRF9TVEFUVVNfRVJSX01BU0sJR0VO
TUFTSyg3LA0KPiA+PiAwKQ0KPiA+Pg0KPiA+PiAgIGVudW0gew0KPiA+PiAgIAlCQVNFX0RBVEEg
PSAwLA0KPiA+PiBAQCAtMzksMTg3ICszOSw0MiBAQCBlbnVtIHsNCj4gPj4NCj4gPj4gICB0eXBl
ZGVmIHN0cnVjdCB7DQo+ID4+ICAgCXN0cnVjdCBkZXZpY2UgKmRldjsNCj4gPj4gLQlzdHJ1Y3Qg
bXV0ZXggbG9jazsNCj4gPj4gLQlpbnQgaXJxOw0KPiA+PiAtCXN0cnVjdCBjb21wbGV0aW9uIGNt
ZF9jb21wbGV0ZTsNCj4gPj4gICAJLyogYmFzZSBvZiBpbnRlcmZhY2UgYW5kIGRhdGEgcmVnaXN0
ZXJzICovDQo+ID4+ICAgCXZvaWQgX19pb21lbSAqYmFzZVtSRVNFUlZFRF9JUENdW0JBU0VfTUFY
XTsNCj4gPj4gKwlzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaXBjX2RldltSRVNFUlZFRF9JUENdOw0K
PiA+PiAgIAlJUENfVFlQRSB0eXBlOw0KPiA+PiAgIH0gSVBDX0RFVjsNCj4gPj4NCj4gPj4gICBz
dGF0aWMgSVBDX0RFViAqcHVuaXRfaXBjZGV2Ow0KPiA+Pg0KPiA+PiAtc3RhdGljIGlubGluZSB1
MzIgaXBjX3JlYWRfc3RhdHVzKElQQ19ERVYgKmlwY2RldiwgSVBDX1RZUEUgdHlwZSkgLXsNCj4g
Pj4gLQlyZXR1cm4gcmVhZGwoaXBjZGV2LT5iYXNlW3R5cGVdW0JBU0VfSUZBQ0VdKTsNCj4gPj4g
LX0NCj4gPj4gLQ0KPiA+PiAtc3RhdGljIGlubGluZSB2b2lkIGlwY193cml0ZV9jbWQoSVBDX0RF
ViAqaXBjZGV2LCBJUENfVFlQRSB0eXBlLCB1MzINCj4gPj4gY21kKSAtIHsNCj4gPj4gLQl3cml0
ZWwoY21kLCBpcGNkZXYtPmJhc2VbdHlwZV1bQkFTRV9JRkFDRV0pOw0KPiA+PiAtfQ0KPiA+PiAt
DQo+ID4+IC1zdGF0aWMgaW5saW5lIHUzMiBpcGNfcmVhZF9kYXRhX2xvdyhJUENfREVWICppcGNk
ZXYsIElQQ19UWVBFIHR5cGUpIC17DQo+ID4+IC0JcmV0dXJuIHJlYWRsKGlwY2Rldi0+YmFzZVt0
eXBlXVtCQVNFX0RBVEFdICsgT0ZGU0VUX0RBVEFfTE9XKTsNCj4gPj4gLX0NCj4gPj4gLQ0KPiA+
PiAtc3RhdGljIGlubGluZSB1MzIgaXBjX3JlYWRfZGF0YV9oaWdoKElQQ19ERVYgKmlwY2Rldiwg
SVBDX1RZUEUgdHlwZSkgLXsNCj4gPj4gLQlyZXR1cm4gcmVhZGwoaXBjZGV2LT5iYXNlW3R5cGVd
W0JBU0VfREFUQV0gKyBPRkZTRVRfREFUQV9ISUdIKTsNCj4gPj4gLX0NCj4gPj4gLQ0KPiA+PiAt
c3RhdGljIGlubGluZSB2b2lkIGlwY193cml0ZV9kYXRhX2xvdyhJUENfREVWICppcGNkZXYsIElQ
Q19UWVBFDQo+ID4+IHR5cGUsIHUzMg0KPiA+PiBkYXRhKSAtew0KPiA+PiAtCXdyaXRlbChkYXRh
LCBpcGNkZXYtPmJhc2VbdHlwZV1bQkFTRV9EQVRBXSArIE9GRlNFVF9EQVRBX0xPVyk7DQo+ID4+
IC19DQo+ID4+IC0NCj4gPj4gLXN0YXRpYyBpbmxpbmUgdm9pZCBpcGNfd3JpdGVfZGF0YV9oaWdo
KElQQ19ERVYgKmlwY2RldiwgSVBDX1RZUEUNCj4gPj4gdHlwZSwgdTMyDQo+ID4+IGRhdGEpIC17
DQo+ID4+IC0Jd3JpdGVsKGRhdGEsIGlwY2Rldi0+YmFzZVt0eXBlXVtCQVNFX0RBVEFdICsgT0ZG
U0VUX0RBVEFfSElHSCk7DQo+ID4+IC19DQo+ID4+ICtjb25zdCBjaGFyICppcGNfZGV2X25hbWVb
UkVTRVJWRURfSVBDXSA9IHsNCj4gPj4gKwlQVU5JVF9CSU9TX0lQQ19ERVYsDQo+ID4+ICsJUFVO
SVRfR1REX0lQQ19ERVYsDQo+ID4+ICsJUFVOSVRfSVNQX0lQQ19ERVYNCj4gPj4gK307DQo+ID4+
DQo+ID4+IC1zdGF0aWMgY29uc3QgY2hhciAqaXBjX2Vycl9zdHJpbmcoaW50IGVycm9yKSAtew0K
PiA+PiAtCWlmIChlcnJvciA9PSBJUENfUFVOSVRfRVJSX1NVQ0NFU1MpDQo+ID4+IC0JCXJldHVy
biAibm8gZXJyb3IiOw0KPiA+PiAtCWVsc2UgaWYgKGVycm9yID09IElQQ19QVU5JVF9FUlJfSU5W
QUxJRF9DTUQpDQo+ID4+IC0JCXJldHVybiAiaW52YWxpZCBjb21tYW5kIjsNCj4gPj4gLQllbHNl
IGlmIChlcnJvciA9PSBJUENfUFVOSVRfRVJSX0lOVkFMSURfUEFSQU1FVEVSKQ0KPiA+PiAtCQly
ZXR1cm4gImludmFsaWQgcGFyYW1ldGVyIjsNCj4gPj4gLQllbHNlIGlmIChlcnJvciA9PSBJUENf
UFVOSVRfRVJSX0NNRF9USU1FT1VUKQ0KPiA+PiAtCQlyZXR1cm4gImNvbW1hbmQgdGltZW91dCI7
DQo+ID4+IC0JZWxzZSBpZiAoZXJyb3IgPT0gSVBDX1BVTklUX0VSUl9DTURfTE9DS0VEKQ0KPiA+
PiAtCQlyZXR1cm4gImNvbW1hbmQgbG9ja2VkIjsNCj4gPj4gLQllbHNlIGlmIChlcnJvciA9PSBJ
UENfUFVOSVRfRVJSX0lOVkFMSURfVlJfSUQpDQo+ID4+IC0JCXJldHVybiAiaW52YWxpZCB2ciBp
ZCI7DQo+ID4+IC0JZWxzZSBpZiAoZXJyb3IgPT0gSVBDX1BVTklUX0VSUl9WUl9FUlIpDQo+ID4+
IC0JCXJldHVybiAidnIgZXJyb3IiOw0KPiA+PiAtCWVsc2UNCj4gPj4gLQkJcmV0dXJuICJ1bmtu
b3duIGVycm9yIjsNCj4gPj4gLX0NCj4gPj4gK3N0YXRpYyBzdHJ1Y3QgcmVnbWFwX2NvbmZpZyBw
dW5pdF9yZWdtYXBfY29uZmlnID0gew0KPiA+PiArICAgICAgICAucmVnX2JpdHMgPSAzMiwNCj4g
Pj4gKyAgICAgICAgLnJlZ19zdHJpZGUgPSA0LA0KPiA+PiArICAgICAgICAudmFsX2JpdHMgPSAz
MiwNCj4gPj4gK307DQo+ID4+DQo+ID4+IC1zdGF0aWMgaW50IGludGVsX3B1bml0X2lwY19jaGVj
a19zdGF0dXMoSVBDX0RFViAqaXBjZGV2LCBJUENfVFlQRQ0KPiA+PiB0eXBlKQ0KPiA+PiAraW50
IHByZV9zaW1wbGVfY21kX2ZuKHUzMiAqY21kX2xpc3QsIHUzMiBjbWRsZW4pDQo+ID4+ICAgew0K
PiA+PiAtCWludCBsb29wcyA9IENNRF9USU1FT1VUX1NFQ09ORFMgKiBVU0VDX1BFUl9TRUM7DQo+
ID4+IC0JaW50IGVycmNvZGU7DQo+ID4+IC0JaW50IHN0YXR1czsNCj4gPj4gLQ0KPiA+PiAtCWlm
IChpcGNkZXYtPmlycSkgew0KPiA+PiAtCQlpZiAoIXdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91
dCgmaXBjZGV2LT5jbWRfY29tcGxldGUsDQo+ID4+IC0JCQkJCQkgQ01EX1RJTUVPVVRfU0VDT05E
UyAqDQo+ID4+IEhaKSkgew0KPiA+PiAtCQkJZGV2X2VycihpcGNkZXYtPmRldiwgIklQQyB0aW1l
ZCBvdXRcbiIpOw0KPiA+PiAtCQkJcmV0dXJuIC1FVElNRURPVVQ7DQo+ID4+IC0JCX0NCj4gPj4g
LQl9IGVsc2Ugew0KPiA+PiAtCQl3aGlsZSAoKGlwY19yZWFkX3N0YXR1cyhpcGNkZXYsIHR5cGUp
ICYgQ01EX1JVTikgJiYgLS0NCj4gPj4gbG9vcHMpDQo+ID4+IC0JCQl1ZGVsYXkoMSk7DQo+ID4+
IC0JCWlmICghbG9vcHMpIHsNCj4gPj4gLQkJCWRldl9lcnIoaXBjZGV2LT5kZXYsICJJUEMgdGlt
ZWQgb3V0XG4iKTsNCj4gPj4gLQkJCXJldHVybiAtRVRJTUVET1VUOw0KPiA+PiAtCQl9DQo+ID4+
IC0JfQ0KPiA+PiArCWlmICghY21kX2xpc3QgfHwgY21kbGVuICE9IFBVTklUX1BBUkFNX0xFTikN
Cj4gPj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+DQo+ID4+IC0Jc3RhdHVzID0gaXBjX3JlYWRf
c3RhdHVzKGlwY2RldiwgdHlwZSk7DQo+ID4+IC0JZXJyY29kZSA9IHN0YXR1cyAmIENNRF9FUlJD
T0RFX01BU0s7DQo+ID4+IC0JaWYgKGVycmNvZGUpIHsNCj4gPj4gLQkJZGV2X2VycihpcGNkZXYt
PmRldiwgIklQQyBmYWlsZWQ6ICVzLCBJUENfU1RTPTB4JXhcbiIsDQo+ID4+IC0JCQlpcGNfZXJy
X3N0cmluZyhlcnJjb2RlKSwgc3RhdHVzKTsNCj4gPj4gLQkJcmV0dXJuIC1FSU87DQo+ID4+IC0J
fQ0KPiA+PiArCWNtZF9saXN0WzBdIHw9IENNRF9SVU4gfCBjbWRfbGlzdFsxXSA8PCBDTURfUEFS
QTFfU0hJRlQgfA0KPiA+PiArCQljbWRfbGlzdFsyXSA8PCBDTURfUEFSQTFfU0hJRlQ7DQo+ID4+
DQo+ID4+ICAgCXJldHVybiAwOw0KPiA+PiAgIH0NCj4gPj4NCj4gPj4gLS8qKg0KPiA+PiAtICog
aW50ZWxfcHVuaXRfaXBjX3NpbXBsZV9jb21tYW5kKCkgLSBTaW1wbGUgSVBDIGNvbW1hbmQNCj4g
Pj4gLSAqIEBjbWQ6CUlQQyBjb21tYW5kIGNvZGUuDQo+ID4+IC0gKiBAcGFyYTE6CUZpcnN0IDhi
aXQgcGFyYW1ldGVyLCBzZXQgMCBpZiBub3QgdXNlZC4NCj4gPj4gLSAqIEBwYXJhMjoJU2Vjb25k
IDhiaXQgcGFyYW1ldGVyLCBzZXQgMCBpZiBub3QgdXNlZC4NCj4gPj4gLSAqDQo+ID4+IC0gKiBT
ZW5kIGEgSVBDIGNvbW1hbmQgdG8gUC1Vbml0IHdoZW4gdGhlcmUgaXMgbm8gZGF0YSB0cmFuc2Fj
dGlvbg0KPiA+PiAtICoNCj4gPj4gLSAqIFJldHVybjoJSVBDIGVycm9yIGNvZGUgb3IgMCBvbiBz
dWNjZXNzLg0KPiA+PiAtICovDQo+ID4+IC1pbnQgaW50ZWxfcHVuaXRfaXBjX3NpbXBsZV9jb21t
YW5kKGludCBjbWQsIGludCBwYXJhMSwgaW50IHBhcmEyKSAtew0KPiA+PiAtCUlQQ19ERVYgKmlw
Y2RldiA9IHB1bml0X2lwY2RldjsNCj4gPj4gLQlJUENfVFlQRSB0eXBlOw0KPiA+PiAtCXUzMiB2
YWw7DQo+ID4+IC0JaW50IHJldDsNCj4gPj4gLQ0KPiA+PiAtCW11dGV4X2xvY2soJmlwY2Rldi0+
bG9jayk7DQo+ID4+IC0NCj4gPj4gLQlyZWluaXRfY29tcGxldGlvbigmaXBjZGV2LT5jbWRfY29t
cGxldGUpOw0KPiA+PiAtCXR5cGUgPSAoY21kICYgSVBDX1BVTklUX0NNRF9UWVBFX01BU0spID4+
IElQQ19UWVBFX09GRlNFVDsNCj4gPj4gLQ0KPiA+PiAtCXZhbCA9IGNtZCAmIH5JUENfUFVOSVRf
Q01EX1RZUEVfTUFTSzsNCj4gPj4gLQl2YWwgfD0gQ01EX1JVTiB8IHBhcmEyIDw8IENNRF9QQVJB
Ml9TSElGVCB8IHBhcmExIDw8DQo+ID4+IENNRF9QQVJBMV9TSElGVDsNCj4gPj4gLQlpcGNfd3Jp
dGVfY21kKGlwY2RldiwgdHlwZSwgdmFsKTsNCj4gPj4gLQlyZXQgPSBpbnRlbF9wdW5pdF9pcGNf
Y2hlY2tfc3RhdHVzKGlwY2RldiwgdHlwZSk7DQo+ID4+IC0NCj4gPj4gLQltdXRleF91bmxvY2so
JmlwY2Rldi0+bG9jayk7DQo+ID4+IC0NCj4gPj4gLQlyZXR1cm4gcmV0Ow0KPiA+PiAtfQ0KPiA+
PiAtRVhQT1JUX1NZTUJPTChpbnRlbF9wdW5pdF9pcGNfc2ltcGxlX2NvbW1hbmQpOw0KPiA+PiAt
DQo+ID4+IC0vKioNCj4gPj4gLSAqIGludGVsX3B1bml0X2lwY19jb21tYW5kKCkgLSBJUEMgY29t
bWFuZCB3aXRoIGRhdGEgYW5kIHBvaW50ZXJzDQo+ID4+IC0gKiBAY21kOglJUEMgY29tbWFuZCBj
b2RlLg0KPiA+PiAtICogQHBhcmExOglGaXJzdCA4Yml0IHBhcmFtZXRlciwgc2V0IDAgaWYgbm90
IHVzZWQuDQo+ID4+IC0gKiBAcGFyYTI6CVNlY29uZCA4Yml0IHBhcmFtZXRlciwgc2V0IDAgaWYg
bm90IHVzZWQuDQo+ID4+IC0gKiBAaW46CQlJbnB1dCBkYXRhLCAzMmJpdCBmb3IgQklPUyBjbWQs
IHR3byAzMmJpdCBmb3IgR1REDQo+ID4+IGFuZCBJU1BELg0KPiA+PiAtICogQG91dDoJT3V0cHV0
IGRhdGEuDQo+ID4+IC0gKg0KPiA+PiAtICogU2VuZCBhIElQQyBjb21tYW5kIHRvIFAtVW5pdCB3
aXRoIGRhdGEgdHJhbnNhY3Rpb24NCj4gPj4gLSAqDQo+ID4+IC0gKiBSZXR1cm46CUlQQyBlcnJv
ciBjb2RlIG9yIDAgb24gc3VjY2Vzcy4NCj4gPj4gLSAqLw0KPiA+PiAtaW50IGludGVsX3B1bml0
X2lwY19jb21tYW5kKHUzMiBjbWQsIHUzMiBwYXJhMSwgdTMyIHBhcmEyLCB1MzIgKmluLA0KPiA+
PiB1MzINCj4gPj4gKm91dCkgLXsNCj4gPj4gLQlJUENfREVWICppcGNkZXYgPSBwdW5pdF9pcGNk
ZXY7DQo+ID4+IC0JSVBDX1RZUEUgdHlwZTsNCj4gPj4gLQl1MzIgdmFsOw0KPiA+PiAtCWludCBy
ZXQ7DQo+ID4+IC0NCj4gPj4gLQltdXRleF9sb2NrKCZpcGNkZXYtPmxvY2spOw0KPiA+PiAtDQo+
ID4+IC0JcmVpbml0X2NvbXBsZXRpb24oJmlwY2Rldi0+Y21kX2NvbXBsZXRlKTsNCj4gPj4gLQl0
eXBlID0gKGNtZCAmIElQQ19QVU5JVF9DTURfVFlQRV9NQVNLKSA+PiBJUENfVFlQRV9PRkZTRVQ7
DQo+ID4+IC0NCj4gPj4gLQlpZiAoaW4pIHsNCj4gPj4gLQkJaXBjX3dyaXRlX2RhdGFfbG93KGlw
Y2RldiwgdHlwZSwgKmluKTsNCj4gPj4gLQkJaWYgKHR5cGUgPT0gR1REUklWRVJfSVBDIHx8IHR5
cGUgPT0gSVNQRFJJVkVSX0lQQykNCj4gPj4gLQkJCWlwY193cml0ZV9kYXRhX2hpZ2goaXBjZGV2
LCB0eXBlLCAqKytpbik7DQo+ID4+IC0JfQ0KPiA+PiAtDQo+ID4+IC0JdmFsID0gY21kICYgfklQ
Q19QVU5JVF9DTURfVFlQRV9NQVNLOw0KPiA+PiAtCXZhbCB8PSBDTURfUlVOIHwgcGFyYTIgPDwg
Q01EX1BBUkEyX1NISUZUIHwgcGFyYTEgPDwNCj4gPj4gQ01EX1BBUkExX1NISUZUOw0KPiA+PiAt
CWlwY193cml0ZV9jbWQoaXBjZGV2LCB0eXBlLCB2YWwpOw0KPiA+PiAtDQo+ID4+IC0JcmV0ID0g
aW50ZWxfcHVuaXRfaXBjX2NoZWNrX3N0YXR1cyhpcGNkZXYsIHR5cGUpOw0KPiA+PiAtCWlmIChy
ZXQpDQo+ID4+IC0JCWdvdG8gb3V0Ow0KPiA+PiAtDQo+ID4+IC0JaWYgKG91dCkgew0KPiA+PiAt
CQkqb3V0ID0gaXBjX3JlYWRfZGF0YV9sb3coaXBjZGV2LCB0eXBlKTsNCj4gPj4gLQkJaWYgKHR5
cGUgPT0gR1REUklWRVJfSVBDIHx8IHR5cGUgPT0gSVNQRFJJVkVSX0lQQykNCj4gPj4gLQkJCSor
K291dCA9IGlwY19yZWFkX2RhdGFfaGlnaChpcGNkZXYsIHR5cGUpOw0KPiA+PiAtCX0NCj4gPj4g
LQ0KPiA+PiAtb3V0Og0KPiA+PiAtCW11dGV4X3VubG9jaygmaXBjZGV2LT5sb2NrKTsNCj4gPj4g
LQlyZXR1cm4gcmV0Ow0KPiA+PiAtfQ0KPiA+PiAtRVhQT1JUX1NZTUJPTF9HUEwoaW50ZWxfcHVu
aXRfaXBjX2NvbW1hbmQpOw0KPiA+PiAtDQo+ID4+IC1zdGF0aWMgaXJxcmV0dXJuX3QgaW50ZWxf
cHVuaXRfaW9jKGludCBpcnEsIHZvaWQgKmRldl9pZCkNCj4gPj4gKy8qIElucHV0IGRhdGEsIDMy
Yml0IGZvciBCSU9TIGNtZCwgdHdvIDMyYml0IGZvciBHVEQgYW5kIElTUEQuICovDQo+ID4+ICtp
bnQNCj4gPj4gK3ByZV9yYXdfY21kX2ZuKHUzMiAqY21kX2xpc3QsIHUzMiBjbWRsZW4sIHU4ICpp
biwgdTMyIGlubGVuLCB1MzIgKm91dCwNCj4gPj4gKwkJdTMyIG91dGxlbiwgdTMyIGRwdHIsIHUz
MiBzcHRyKQ0KPiA+PiAgIHsNCj4gPj4gLQlJUENfREVWICppcGNkZXYgPSBkZXZfaWQ7DQo+ID4+
IC0NCj4gPj4gLQljb21wbGV0ZSgmaXBjZGV2LT5jbWRfY29tcGxldGUpOw0KPiA+PiAtCXJldHVy
biBJUlFfSEFORExFRDsNCj4gPj4gKwlyZXR1cm4gcHJlX3NpbXBsZV9jbWRfZm4oY21kX2xpc3Qs
IGNtZGxlbik7DQo+ID4+ICAgfQ0KPiA+Pg0KPiA+PiAgIHN0YXRpYyBpbnQgaW50ZWxfcHVuaXRf
Z2V0X2JhcnMoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgQEANCj4gPj4gLTI4Miw5DQo+
ID4+ICsxMzcsNzcgQEAgc3RhdGljIGludCBpbnRlbF9wdW5pdF9nZXRfYmFycyhzdHJ1Y3QgcGxh
dGZvcm1fZGV2aWNlDQo+ID4+ICsqcGRldikNCj4gPj4gICAJcmV0dXJuIDA7DQo+ID4+ICAgfQ0K
PiA+Pg0KPiA+PiArc3RhdGljIGludCBwdW5pdF9pcGNfZXJyX2NvZGUoaW50IHN0YXR1cykgew0K
PiA+PiArCXJldHVybiAoc3RhdHVzICYgQ01EX0VSUkNPREVfTUFTSyk7IH0NCj4gPj4gKw0KPiA+
PiArc3RhdGljIGludCBwdW5pdF9pcGNfYnVzeV9jaGVjayhpbnQgc3RhdHVzKSB7DQo+ID4+ICsJ
cmV0dXJuIHN0YXR1cyB8IENNRF9SVU47DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBz
dHJ1Y3QgaW50ZWxfaXBjX2RldiAqaW50ZWxfcHVuaXRfaXBjX2Rldl9jcmVhdGUoc3RydWN0IGRl
dmljZQ0KPiAqZGV2LA0KPiA+PiArCQljb25zdCBjaGFyICpkZXZuYW1lLA0KPiA+PiArCQlpbnQg
aXJxLA0KPiA+PiArCQl2b2lkIF9faW9tZW0gKmJhc2UsDQo+ID4+ICsJCXZvaWQgX19pb21lbSAq
ZGF0YSkNCj4gPj4gK3sNCj4gPj4gKwlzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9vcHMgKm9wczsNCj4g
Pj4gKwlzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9jZmcgKmNmZzsNCj4gPj4gKwlzdHJ1Y3QgcmVnbWFw
ICpjbWRfcmVncywgKmRhdGFfcmVnczsNCj4gPj4gKw0KPiA+PiArICAgICAgICBjZmcgPSBkZXZt
X2t6YWxsb2MoZGV2LCBzaXplb2YoKmNmZyksIEdGUF9LRVJORUwpOw0KPiA+PiArICAgICAgICBp
ZiAoIWNmZykNCj4gPj4gKyAgICAgICAgICAgICAgICByZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsN
Cj4gPj4gKw0KPiA+PiArCW9wcyA9IGRldm1fa3phbGxvYyhkZXYsIHNpemVvZigqb3BzKSwgR0ZQ
X0tFUk5FTCk7DQo+ID4+ICsJaWYgKCFvcHMpDQo+ID4+ICsJCXJldHVybiBFUlJfUFRSKC1FTk9N
RU0pOw0KPiA+PiArDQo+ID4+ICsJcHVuaXRfcmVnbWFwX2NvbmZpZy5uYW1lID0gZGV2bV9rYXNw
cmludGYoZGV2LCBHRlBfS0VSTkVMLA0KPiA+PiAiJXNfJXMiLA0KPiA+PiArCQkJCQkJICBkZXZu
YW1lLCAiYmFzZSIpOw0KPiA+PiArDQo+ID4+ICsJY21kX3JlZ3MgPSBkZXZtX3JlZ21hcF9pbml0
X21taW9fY2xrKGRldiwgTlVMTCwgYmFzZSwNCj4gPj4gKwkJCSZwdW5pdF9yZWdtYXBfY29uZmln
KTsNCj4gPj4gKwlpZiAoSVNfRVJSKGNtZF9yZWdzKSkgew0KPiA+PiArICAgICAgICAgICAgICAg
IGRldl9lcnIoZGV2LCAiY21kX3JlZ3MgcmVnbWFwIGluaXQgZmFpbGVkXG4iKTsNCj4gPj4gKyAg
ICAgICAgICAgICAgICByZXR1cm4gRVJSX0NBU1QoY21kX3JlZ3MpOzsNCj4gPj4gKyAgICAgICAg
fQ0KPiA+PiArDQo+ID4+ICsJcHVuaXRfcmVnbWFwX2NvbmZpZy5uYW1lID0gZGV2bV9rYXNwcmlu
dGYoZGV2LCBHRlBfS0VSTkVMLA0KPiA+PiAiJXNfJXMiLA0KPiA+PiArCQkJCQkJICBkZXZuYW1l
LCAiZGF0YSIpOw0KPiA+PiArDQo+ID4+ICsgICAgICAgIGRhdGFfcmVncyA9IGRldm1fcmVnbWFw
X2luaXRfbW1pb19jbGsoZGV2LCBOVUxMLCBkYXRhLA0KPiA+PiArCQkJJnB1bml0X3JlZ21hcF9j
b25maWcpOw0KPiA+PiArICAgICAgICBpZiAoSVNfRVJSKGRhdGFfcmVncykpIHsNCj4gPj4gKyAg
ICAgICAgICAgICAgICBkZXZfZXJyKGRldiwgImRhdGFfcmVncyByZWdtYXAgaW5pdCBmYWlsZWRc
biIpOw0KPiA+PiArICAgICAgICAgICAgICAgIHJldHVybiBFUlJfQ0FTVChkYXRhX3JlZ3MpOzsN
Cj4gPj4gKyAgICAgICAgfQ0KPiA+PiArDQo+ID4+ICsJLyogc2V0IElQQyBkZXYgb3BzICovDQo+
ID4+ICsJb3BzLT50b19lcnJfY29kZSA9IHB1bml0X2lwY19lcnJfY29kZTsNCj4gPj4gKwlvcHMt
PmJ1c3lfY2hlY2sgPSBwdW5pdF9pcGNfYnVzeV9jaGVjazsNCj4gPj4gKwlvcHMtPnByZV9zaW1w
bGVfY21kX2ZuID0gcHJlX3NpbXBsZV9jbWRfZm47DQo+ID4+ICsJb3BzLT5wcmVfcmF3X2NtZF9m
biA9IHByZV9yYXdfY21kX2ZuOw0KPiA+PiArDQo+ID4+ICsJaWYgKGlycSA+IDApDQo+ID4+ICsJ
ICAgICAgICBjZmctPm1vZGUgPSBJUENfREVWX01PREVfSVJROw0KPiA+PiArCWVsc2UNCj4gPj4g
KwkgICAgICAgIGNmZy0+bW9kZSA9IElQQ19ERVZfTU9ERV9QT0xMSU5HOw0KPiA+PiArDQo+ID4+
ICsJY2ZnLT5jaGFuX3R5cGUgPSBJUENfQ0hBTk5FTF9JQV9QVU5JVDsNCj4gPj4gKwljZmctPmly
cSA9IGlycTsNCj4gPj4gKwljZmctPmlycWZsYWdzID0gSVJRRl9OT19TVVNQRU5EIHwgSVJRRl9T
SEFSRUQ7DQo+ID4+ICsJY2ZnLT5jbWRfcmVncyA9IGNtZF9yZWdzOw0KPiA+PiArCWNmZy0+ZGF0
YV9yZWdzID0gZGF0YV9yZWdzOw0KPiA+PiArDQo+ID4+ICsJcmV0dXJuIGRldm1faW50ZWxfaXBj
X2Rldl9jcmVhdGUoZGV2LCBkZXZuYW1lLCBjZmcsIG9wcyk7IH0NCj4gPj4gKw0KPiA+PiAgIHN0
YXRpYyBpbnQgaW50ZWxfcHVuaXRfaXBjX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBk
ZXYpICB7DQo+ID4+IC0JaW50IGlycSwgcmV0Ow0KPiA+PiArCWludCBpcnEsIHJldCwgaTsNCj4g
Pj4NCj4gPj4gICAJcHVuaXRfaXBjZGV2ID0gZGV2bV9remFsbG9jKCZwZGV2LT5kZXYsDQo+ID4+
ICAgCQkJCSAgICBzaXplb2YoKnB1bml0X2lwY2RldiksIEdGUF9LRVJORUwpOyBAQCAtDQo+ID4+
IDI5NCwzNSArMjE3LDMwIEBAIHN0YXRpYyBpbnQgaW50ZWxfcHVuaXRfaXBjX3Byb2JlKHN0cnVj
dA0KPiA+PiBwbGF0Zm9ybV9kZXZpY2UNCj4gPj4gKnBkZXYpDQo+ID4+ICAgCXBsYXRmb3JtX3Nl
dF9kcnZkYXRhKHBkZXYsIHB1bml0X2lwY2Rldik7DQo+ID4+DQo+ID4+ICAgCWlycSA9IHBsYXRm
b3JtX2dldF9pcnEocGRldiwgMCk7DQo+ID4+IC0JaWYgKGlycSA8IDApIHsNCj4gPj4gLQkJcHVu
aXRfaXBjZGV2LT5pcnEgPSAwOw0KPiA+PiAtCQlkZXZfd2FybigmcGRldi0+ZGV2LCAiSW52YWxp
ZCBJUlEsIHVzaW5nIHBvbGxpbmcgbW9kZVxuIik7DQo+ID4+IC0JfSBlbHNlIHsNCj4gPj4gLQkJ
cmV0ID0gZGV2bV9yZXF1ZXN0X2lycSgmcGRldi0+ZGV2LCBpcnEsIGludGVsX3B1bml0X2lvYywN
Cj4gPj4gLQkJCQkgICAgICAgSVJRRl9OT19TVVNQRU5ELCAiaW50ZWxfcHVuaXRfaXBjIiwNCj4g
Pj4gLQkJCQkgICAgICAgJnB1bml0X2lwY2Rldik7DQo+ID4+IC0JCWlmIChyZXQpIHsNCj4gPj4g
LQkJCWRldl9lcnIoJnBkZXYtPmRldiwgIkZhaWxlZCB0byByZXF1ZXN0IGlycTogJWRcbiIsDQo+
ID4+IGlycSk7DQo+ID4+IC0JCQlyZXR1cm4gcmV0Ow0KPiA+PiAtCQl9DQo+ID4+IC0JCXB1bml0
X2lwY2Rldi0+aXJxID0gaXJxOw0KPiA+PiAtCX0NCj4gPj4NCj4gPj4gICAJcmV0ID0gaW50ZWxf
cHVuaXRfZ2V0X2JhcnMocGRldik7DQo+ID4+ICAgCWlmIChyZXQpDQo+ID4+IC0JCWdvdG8gb3V0
Ow0KPiA+PiArCQlyZXR1cm4gcmV0Ow0KPiA+PiArDQo+ID4+ICsJZm9yIChpID0gMDsgaSA8IFJF
U0VSVkVEX0lQQzsgaSsrKSB7DQo+ID4+ICsJCXB1bml0X2lwY2Rldi0+aXBjX2RldltpXSA9IGlu
dGVsX3B1bml0X2lwY19kZXZfY3JlYXRlKA0KPiA+PiArCQkJCSZwZGV2LT5kZXYsDQo+ID4+ICsJ
CQkJaXBjX2Rldl9uYW1lW2ldLA0KPiA+PiArCQkJCWlycSwNCj4gPj4gKwkJCQlwdW5pdF9pcGNk
ZXYtPmJhc2VbaV1bQkFTRV9JRkFDRV0sDQo+ID4+ICsJCQkJcHVuaXRfaXBjZGV2LT5iYXNlW2ld
W0JBU0VfREFUQV0pOw0KPiA+PiArDQo+ID4+ICsJCWlmIChJU19FUlIocHVuaXRfaXBjZGV2LT5p
cGNfZGV2W2ldKSkgew0KPiA+PiArCQkJZGV2X2VycigmcGRldi0+ZGV2LCAiJXMgY3JlYXRlIGZh
aWxlZFxuIiwNCj4gPj4gKwkJCQkJaXBjX2Rldl9uYW1lW2ldKTsNCj4gPj4gKwkJCXJldHVybiBQ
VFJfRVJSKHB1bml0X2lwY2Rldi0+aXBjX2RldltpXSk7DQo+ID4+ICsJCX0NCj4gPj4gKwl9DQo+
ID4+DQo+ID4+ICAgCXB1bml0X2lwY2Rldi0+ZGV2ID0gJnBkZXYtPmRldjsNCj4gPj4gLQltdXRl
eF9pbml0KCZwdW5pdF9pcGNkZXYtPmxvY2spOw0KPiA+PiAtCWluaXRfY29tcGxldGlvbigmcHVu
aXRfaXBjZGV2LT5jbWRfY29tcGxldGUpOw0KPiA+Pg0KPiA+PiAtb3V0Og0KPiA+PiAgIAlyZXR1
cm4gcmV0Ow0KPiA+PiAtfQ0KPiA+Pg0KPiA+PiAtc3RhdGljIGludCBpbnRlbF9wdW5pdF9pcGNf
cmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpIC17DQo+ID4+IC0JcmV0dXJuIDA7
DQo+ID4+ICAgfQ0KPiA+Pg0KPiA+PiAgIHN0YXRpYyBjb25zdCBzdHJ1Y3QgYWNwaV9kZXZpY2Vf
aWQgcHVuaXRfaXBjX2FjcGlfaWRzW10gPSB7IEBADQo+ID4+IC0zMzIsNyArMjUwLDYgQEAgc3Rh
dGljIGNvbnN0IHN0cnVjdCBhY3BpX2RldmljZV9pZA0KPiA+PiBwdW5pdF9pcGNfYWNwaV9pZHNb
XSA9IHsNCj4gPj4NCj4gPj4gICBzdGF0aWMgc3RydWN0IHBsYXRmb3JtX2RyaXZlciBpbnRlbF9w
dW5pdF9pcGNfZHJpdmVyID0gew0KPiA+PiAgIAkucHJvYmUgPSBpbnRlbF9wdW5pdF9pcGNfcHJv
YmUsDQo+ID4+IC0JLnJlbW92ZSA9IGludGVsX3B1bml0X2lwY19yZW1vdmUsDQo+ID4+ICAgCS5k
cml2ZXIgPSB7DQo+ID4+ICAgCQkubmFtZSA9ICJpbnRlbF9wdW5pdF9pcGMiLA0KPiA+PiAgIAkJ
LmFjcGlfbWF0Y2hfdGFibGUgPSBBQ1BJX1BUUihwdW5pdF9pcGNfYWNwaV9pZHMpLCBkaWZmIC0t
Z2l0DQo+ID4+IGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvaW50ZWxfdGVsZW1ldHJ5X3BsdGRydi5j
DQo+ID4+IGIvZHJpdmVycy9wbGF0Zm9ybS94ODYvaW50ZWxfdGVsZW1ldHJ5X3BsdGRydi5jDQo+
ID4+IGluZGV4IGUwNDI0ZDUuLmJmODI4NGEgMTAwNjQ0DQo+ID4+IC0tLSBhL2RyaXZlcnMvcGxh
dGZvcm0veDg2L2ludGVsX3RlbGVtZXRyeV9wbHRkcnYuYw0KPiA+PiArKysgYi9kcml2ZXJzL3Bs
YXRmb3JtL3g4Ni9pbnRlbF90ZWxlbWV0cnlfcGx0ZHJ2LmMNCj4gPiBUaGlzIGlzIGEgbG9naWNh
bCBzZXBhcmF0aW9uIGFuZCBzbyBzaG91bGQgYmUgYSBkaWZmZXJlbnQgcGF0Y2guDQo+IEJ1dCBp
ZiB3ZSBzcGxpdCBpdCBpbnRvIHR3byBkaWZmZXJlbnQgcGF0Y2hlcyB0aGVuIGl0IHdpbGwgYnJl
YWsgdGhlIGJpc2VjdC0NCj4gYWJpbGl0eSBvZiB0aGVzZSBwYXRjaGVzLg0KPiA+DQo+ID4+IEBA
IC05OCw2ICs5OCw3IEBAIHN0cnVjdCB0ZWxlbV9zc3JhbV9yZWdpb24geyAgfTsNCj4gPj4NCj4g
Pj4gICBzdGF0aWMgc3RydWN0IHRlbGVtZXRyeV9wbHRfY29uZmlnICp0ZWxtX2NvbmY7DQo+ID4+
ICtzdGF0aWMgc3RydWN0IGludGVsX2lwY19kZXYgKnB1bml0X2Jpb3NfaXBjX2RldjsNCj4gPiBT
aW1wbHkgcHVuaXRfaXBjX2RldiBpcyBnb29kIGVub3VnaC4NCj4gVGhlcmUgYXJlIHRocmVlIFBV
TklUIElQQyBkZXZpY2VzLiBTbyB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gdGhlbSBJIGFkZGVk
DQo+IGV4dHJhIHN0cmluZyB0byBpdC4NCg0KT0sgdGhhdCBtYWtlcyBzZW5zZS4gDQpIb3dldmVy
IHB1bml0X2Jpb3NfaXBjIGlzIG5laXRoZXIgdGhlIGFjdHVhbCBkZXZpY2UgdHlwZS9uYW1lIG5v
ciBpbmRpY2F0ZXMgdGhlIHVzYWdlIG9mIGl0IHNpbmNlIEJJT1MgaGFzIGdvdCBub3RoaW5nIHRv
IGRvIHdpdGggaXQgKGV4Y2VwdCBmb3IgZXhwb3NpbmcgdGhlIFNTUkFNIGJhc2UgYWRkcikuDQpD
YW4gd2UgcmVuYW1lIHRoaXMgdG8gc29tZXRoaW5nIHRoYXQgYWN0dWFsbHkgZGVub3RlcyB3aGF0
IHRoaXMgaXMsIGUuZywgInB1bml0X3RlbGVtZXRyeV9pcGNfZGV2Ii4gU2FtZSBjaGFuZ2UgbmVl
ZHMgdG8gYmUgZG9uZSBpbiBpbnRlbF9wdW5pdF9pcGMuYyBhcyB3ZWxsLiANCg0KPiA+DQo+ID4+
ICAgLyoNCj4gPj4gICAgKiBUaGUgZm9sbG93aW5nIGNvdW50ZXJzIGFyZSBwcm9ncmFtbWVkIGJ5
IGRlZmF1bHQgZHVyaW5nIHNldHVwLg0KPiA+PiBAQCAtMTI3LDcgKzEyOCw2IEBAIHN0YXRpYyBz
dHJ1Y3QgdGVsZW1ldHJ5X2V2dG1hcA0KPiA+PiAgIAl7IlBNQ19TMElYX0JMT0NLX0lQU19DTE9D
S1MiLCAgICAgICAgICAgMHg2MDBCfSwNCj4gPj4gICB9Ow0KPiA+Pg0KPiA+PiAtDQo+ID4+ICAg
c3RhdGljIHN0cnVjdCB0ZWxlbWV0cnlfZXZ0bWFwDQo+ID4+DQo+ID4+IAl0ZWxlbWV0cnlfYXBs
X3Bzc19kZWZhdWx0X2V2ZW50c1tURUxFTV9NQVhfT1NfQUxMT0NBVEVEX0VWRQ0KPiA+PiBOVFNd
ID0gew0KPiA+PiAgIAl7IklBX0NPUkUwX0M2X1JFUyIsCQkJMHgwNDAwfSwNCj4gPj4gQEAgLTI4
MywxMyArMjgzLDEyIEBAIHN0YXRpYyBpbmxpbmUgaW50DQo+ID4+IHRlbGVtZXRyeV9wbHRfY29u
ZmlnX2lvc3NfZXZlbnQodTMyIGV2dF9pZCwgaW50IGluZGV4KSAgc3RhdGljIGlubGluZQ0KPiA+
PiBpbnQNCj4gPj4gdGVsZW1ldHJ5X3BsdF9jb25maWdfcHNzX2V2ZW50KHUzMiBldnRfaWQsIGlu
dCBpbmRleCkgIHsNCj4gPj4gICAJdTMyIHdyaXRlX2J1ZjsNCj4gPj4gLQlpbnQgcmV0Ow0KPiA+
PiArCXUzMiBjbWRbUFVOSVRfUEFSQU1fTEVOXSA9IHswfTsNCj4gPj4NCj4gPj4gICAJd3JpdGVf
YnVmID0gZXZ0X2lkIHwgVEVMRU1fRVZFTlRfRU5BQkxFOw0KPiA+PiAtCXJldCA9DQo+ID4+IGlu
dGVsX3B1bml0X2lwY19jb21tYW5kKElQQ19QVU5JVF9CSU9TX1dSSVRFX1RFTEVfRVZFTlQsDQo+
ID4+IC0JCQkJICAgICAgaW5kZXgsIDAsICZ3cml0ZV9idWYsIE5VTEwpOw0KPiA+PiAtDQo+ID4+
IC0JcmV0dXJuIHJldDsNCj4gPj4gKwlwdW5pdF9jbWRfaW5pdChjbWQsIElQQ19QVU5JVF9CSU9T
X1dSSVRFX1RFTEVfRVZFTlQsIGluZGV4LCAwKTsNCj4gPj4gKwlyZXR1cm4gaXBjX2Rldl9yYXdf
Y21kKHB1bml0X2Jpb3NfaXBjX2RldiwgY21kLA0KPiA+PiBQVU5JVF9QQVJBTV9MRU4sDQo+ID4+
ICsJCQkodTggKikmd3JpdGVfYnVmLCBzaXplb2Yod3JpdGVfYnVmKSwgTlVMTCwgMCwgMCwgMCk7
DQo+ID4+ICAgfQ0KPiA+IDEpIFdoeSB1c2UgcmF3X2NtZCBoZXJlPyBJdCBzaG91bGQgdXNlIGlw
Y19kZXZfY21kKCkgYWNjb3JkaW5nIHRvDQo+IG9yaWdpbmFsIGRlc2lnbi4gU2FtZSBldmVyeXdo
ZXJlIHRob3VnaCB0aGlzIGZpbGUuDQo+IEluIG15IGluaXRpYWwgdmVyc2lvbnMgb2YgdGhpcyBw
YXRjaCBzZXQsIEkgaGFkIG9ubHkgZXhwb3J0ZWQNCj4gaXBjX2Rldl9yYXdfY21kKCkgQVBJLiBT
aW5jZSB0aGlzIGRyaXZlciByZWZhY3RvcmluZyB3b3JrIHdhcyBkb25lIGR1cmluZw0KPiB0aGF0
IHRpbWUsIEkgaGF2ZSB1c2VkIGludGVsX2Rldl9yYXdfY21kKCkgQVBJIGluIGl0LiBJIGhhdmUg
YWRkZWQNCj4gaXBjX2Rldl9jbWQoKSBBUEkgb25seSByZWNlbnRsecKgIHRvIGhhbmRsZSBzb21l
IG5ldyByZXF1aXJlbWVudHMgaW4NCj4gaW50ZWxfc2N1X2lwYy5jIGRyaXZlci4NCj4gDQo+IEkg
d2lsbCBmaXggaXQgaW4gbmV4dCB2ZXJzaW9uLg0KPiANCj4gPiAyKSBwdW5pdF9jbWRfaW5pdCBh
bmQgaXBjX2Rldl9yYXdfY21kIGFyZSByZXBlYXRlZCBtdWx0aXBsZSB0aW1lDQo+ID4gdGhvdWdo
b3V0IHRoZSBmaWxlLiBUaGV5IGNhbiBiZSBtYWRlIGludG8gYSBzZXBhcmF0ZSBsb2NhbCBBUEkg
aW5zaWRlDQo+ID4gdGVsZW1ldHJ5LCBsaWtlIHRlbGVtZXRyeV9wdW5pdF9zZW5kX2NtZCgpLiBT
YW1lIHRob3VnaG91dA0KPiBJIHdpbGwgbWFrZSB0aGlzIGNoYW5nZSBpbiBuZXh0IHZlcnNpb24u
DQo+ID4NCj4gPj4gICBzdGF0aWMgaW50IHRlbGVtZXRyeV9zZXR1cF9pb3NzZXZ0Y29uZmlnKHN0
cnVjdCB0ZWxlbWV0cnlfZXZ0Y29uZmlnDQo+ID4+IGV2dGNvbmZpZywgQEAgLTQzNSw2ICs0MzQs
NyBAQCBzdGF0aWMgaW50DQo+ID4+IHRlbGVtZXRyeV9zZXR1cF9wc3NldnRjb25maWcoc3RydWN0
IHRlbGVtZXRyeV9ldnRjb25maWcgZXZ0Y29uZmlnLA0KPiA+PiAgIAlpbnQgcmV0LCBpbmRleCwg
aWR4Ow0KPiA+PiAgIAl1MzIgKnBzc19ldnRtYXA7DQo+ID4+ICAgCXUzMiB0ZWxlbV9jdHJsOw0K
PiA+PiArCXUzMiBjbWRbUFVOSVRfUEFSQU1fTEVOXSA9IHswfTsNCj4gPj4NCj4gPj4gICAJbnVt
X3Bzc19ldnRzID0gZXZ0Y29uZmlnLm51bV9ldnRzOw0KPiA+PiAgIAlwc3NfcGVyaW9kID0gZXZ0
Y29uZmlnLnBlcmlvZDsNCj4gPj4gQEAgLTQ0Miw4ICs0NDIsOSBAQCBzdGF0aWMgaW50IHRlbGVt
ZXRyeV9zZXR1cF9wc3NldnRjb25maWcoc3RydWN0DQo+ID4+IHRlbGVtZXRyeV9ldnRjb25maWcg
ZXZ0Y29uZmlnLA0KPiA+Pg0KPiA+PiAgIAkvKiBQU1MgQ29uZmlnICovDQo+ID4+ICAgCS8qIEdl
dCB0ZWxlbWV0cnkgRVZFTlQgQ1RMICovDQo+ID4+IC0JcmV0ID0NCj4gPj4gaW50ZWxfcHVuaXRf
aXBjX2NvbW1hbmQoSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0VWRU5UX0NUUkwsDQo+ID4+IC0J
CQkJICAgICAgMCwgMCwgTlVMTCwgJnRlbGVtX2N0cmwpOw0KPiA+PiArCXB1bml0X2NtZF9pbml0
KGNtZCwgSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0VWRU5UX0NUUkwsIDAsDQo+ID4+IDApOw0K
PiA+PiArCXJldCA9IGlwY19kZXZfcmF3X2NtZChwdW5pdF9iaW9zX2lwY19kZXYsIGNtZCwNCj4g
Pj4gUFVOSVRfUEFSQU1fTEVOLCBOVUxMLA0KPiA+PiArCQkJMCwgJnRlbGVtX2N0cmwsIDEsIDAs
IDApOw0KPiA+PiAgIAlpZiAocmV0KSB7DQo+ID4+ICAgCQlwcl9lcnIoIlBTUyBURUxFTV9DVFJM
IFJlYWQgRmFpbGVkXG4iKTsNCj4gPj4gICAJCXJldHVybiByZXQ7DQo+ID4+IEBAIC00NTEsOCAr
NDUyLDkgQEAgc3RhdGljIGludCB0ZWxlbWV0cnlfc2V0dXBfcHNzZXZ0Y29uZmlnKHN0cnVjdA0K
PiA+PiB0ZWxlbWV0cnlfZXZ0Y29uZmlnIGV2dGNvbmZpZywNCj4gPj4NCj4gPj4gICAJLyogRGlz
YWJsZSBUZWxlbWV0cnkgKi8NCj4gPj4gICAJVEVMRU1fRElTQUJMRSh0ZWxlbV9jdHJsKTsNCj4g
Pj4gLQlyZXQgPQ0KPiA+PiBpbnRlbF9wdW5pdF9pcGNfY29tbWFuZChJUENfUFVOSVRfQklPU19X
UklURV9URUxFX0VWRU5UX0NUUkwsDQo+ID4+IC0JCQkJICAgICAgMCwgMCwgJnRlbGVtX2N0cmws
IE5VTEwpOw0KPiA+PiArCXB1bml0X2NtZF9pbml0KGNtZCwgSVBDX1BVTklUX0JJT1NfV1JJVEVf
VEVMRV9FVkVOVF9DVFJMLCAwLA0KPiA+PiAwKTsNCj4gPj4gKwlyZXQgPSBpcGNfZGV2X3Jhd19j
bWQocHVuaXRfYmlvc19pcGNfZGV2LCBjbWQsDQo+ID4+IFBVTklUX1BBUkFNX0xFTiwNCj4gPj4g
KwkJCSh1OCAqKSZ0ZWxlbV9jdHJsLCBzaXplb2YodGVsZW1fY3RybCksIE5VTEwsIDAsIDAsIDAp
Ow0KPiA+PiAgIAlpZiAocmV0KSB7DQo+ID4+ICAgCQlwcl9lcnIoIlBTUyBURUxFTV9DVFJMIEV2
ZW50IERpc2FibGUgV3JpdGUgRmFpbGVkXG4iKTsNCj4gPj4gICAJCXJldHVybiByZXQ7DQo+ID4+
IEBAIC00NjMsOSArNDY1LDEwIEBAIHN0YXRpYyBpbnQgdGVsZW1ldHJ5X3NldHVwX3Bzc2V2dGNv
bmZpZyhzdHJ1Y3QNCj4gPj4gdGVsZW1ldHJ5X2V2dGNvbmZpZyBldnRjb25maWcsDQo+ID4+ICAg
CQkvKiBDbGVhciBBbGwgRXZlbnRzICovDQo+ID4+ICAgCQlURUxFTV9DTEVBUl9FVkVOVFModGVs
ZW1fY3RybCk7DQo+ID4+DQo+ID4+IC0JCXJldCA9IGludGVsX3B1bml0X2lwY19jb21tYW5kKA0K
PiA+PiAtCQkJCUlQQ19QVU5JVF9CSU9TX1dSSVRFX1RFTEVfRVZFTlRfQ1RSTCwNCj4gPj4gLQkJ
CQkwLCAwLCAmdGVsZW1fY3RybCwgTlVMTCk7DQo+ID4+ICsJCXB1bml0X2NtZF9pbml0KGNtZCwN
Cj4gPj4gSVBDX1BVTklUX0JJT1NfV1JJVEVfVEVMRV9FVkVOVF9DVFJMLCAwLCAwKTsNCj4gPj4g
KwkJcmV0ID0gaXBjX2Rldl9yYXdfY21kKHB1bml0X2Jpb3NfaXBjX2RldiwgY21kLA0KPiA+PiBQ
VU5JVF9QQVJBTV9MRU4sDQo+ID4+ICsJCQkJKHU4ICopJnRlbGVtX2N0cmwsIHNpemVvZih0ZWxl
bV9jdHJsKSwgTlVMTCwNCj4gPj4gKwkJCQkwLCAwLCAwKTsNCj4gPj4gICAJCWlmIChyZXQpIHsN
Cj4gPj4gICAJCQlwcl9lcnIoIlBTUyBURUxFTV9DVFJMIEV2ZW50IERpc2FibGUgV3JpdGUNCj4g
RmFpbGVkXG4iKTsNCj4gPj4gICAJCQlyZXR1cm4gcmV0Ow0KPiA+PiBAQCAtNDg5LDkgKzQ5Miwx
MCBAQCBzdGF0aWMgaW50IHRlbGVtZXRyeV9zZXR1cF9wc3NldnRjb25maWcoc3RydWN0DQo+ID4+
IHRlbGVtZXRyeV9ldnRjb25maWcgZXZ0Y29uZmlnLA0KPiA+PiAgIAkJLyogQ2xlYXIgQWxsIEV2
ZW50cyAqLw0KPiA+PiAgIAkJVEVMRU1fQ0xFQVJfRVZFTlRTKHRlbGVtX2N0cmwpOw0KPiA+Pg0K
PiA+PiAtCQlyZXQgPSBpbnRlbF9wdW5pdF9pcGNfY29tbWFuZCgNCj4gPj4gLQkJCQlJUENfUFVO
SVRfQklPU19XUklURV9URUxFX0VWRU5UX0NUUkwsDQo+ID4+IC0JCQkJMCwgMCwgJnRlbGVtX2N0
cmwsIE5VTEwpOw0KPiA+PiArCQlwdW5pdF9jbWRfaW5pdChjbWQsDQo+ID4+IElQQ19QVU5JVF9C
SU9TX1dSSVRFX1RFTEVfRVZFTlRfQ1RSTCwgMCwgMCk7DQo+ID4+ICsJCXJldCA9IGlwY19kZXZf
cmF3X2NtZChwdW5pdF9iaW9zX2lwY19kZXYsIGNtZCwNCj4gPj4gUFVOSVRfUEFSQU1fTEVOLA0K
PiA+PiArCQkJCSh1OCAqKSZ0ZWxlbV9jdHJsLCBzaXplb2YodGVsZW1fY3RybCksIE5VTEwsDQo+
ID4+ICsJCQkJMCwgMCwgMCk7DQo+ID4+ICAgCQlpZiAocmV0KSB7DQo+ID4+ICAgCQkJcHJfZXJy
KCJQU1MgVEVMRU1fQ1RSTCBFdmVudCBEaXNhYmxlIFdyaXRlDQo+IEZhaWxlZFxuIik7DQo+ID4+
ICAgCQkJcmV0dXJuIHJldDsNCj4gPj4gQEAgLTU0MCw4ICs1NDQsOSBAQCBzdGF0aWMgaW50IHRl
bGVtZXRyeV9zZXR1cF9wc3NldnRjb25maWcoc3RydWN0DQo+ID4+IHRlbGVtZXRyeV9ldnRjb25m
aWcgZXZ0Y29uZmlnLA0KPiA+PiAgIAlURUxFTV9FTkFCTEVfUEVSSU9ESUModGVsZW1fY3RybCk7
DQo+ID4+ICAgCXRlbGVtX2N0cmwgfD0gcHNzX3BlcmlvZDsNCj4gPj4NCj4gPj4gLQlyZXQgPQ0K
PiA+PiBpbnRlbF9wdW5pdF9pcGNfY29tbWFuZChJUENfUFVOSVRfQklPU19XUklURV9URUxFX0VW
RU5UX0NUUkwsDQo+ID4+IC0JCQkJICAgICAgMCwgMCwgJnRlbGVtX2N0cmwsIE5VTEwpOw0KPiA+
PiArCXB1bml0X2NtZF9pbml0KGNtZCwgSVBDX1BVTklUX0JJT1NfV1JJVEVfVEVMRV9FVkVOVF9D
VFJMLCAwLA0KPiA+PiAwKTsNCj4gPj4gKwlyZXQgPSBpcGNfZGV2X3Jhd19jbWQocHVuaXRfYmlv
c19pcGNfZGV2LCBjbWQsDQo+ID4+IFBVTklUX1BBUkFNX0xFTiwNCj4gPj4gKwkJCSh1OCAqKSZ0
ZWxlbV9jdHJsLCBzaXplb2YodGVsZW1fY3RybCksIE5VTEwsIDAsIDAsIDApOw0KPiA+PiAgIAlp
ZiAocmV0KSB7DQo+ID4+ICAgCQlwcl9lcnIoIlBTUyBURUxFTV9DVFJMIEV2ZW50IEVuYWJsZSBX
cml0ZSBGYWlsZWRcbiIpOw0KPiA+PiAgIAkJcmV0dXJuIHJldDsNCj4gPj4gQEAgLTYwMSw2ICs2
MDYsNyBAQCBzdGF0aWMgaW50IHRlbGVtZXRyeV9zZXR1cChzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNl
DQo+ID4+ICpwZGV2KSAgew0KPiA+PiAgIAlzdHJ1Y3QgdGVsZW1ldHJ5X2V2dGNvbmZpZyBwc3Nf
ZXZ0Y29uZmlnLCBpb3NzX2V2dGNvbmZpZzsNCj4gPj4gICAJdTMyIHJlYWRfYnVmLCBldmVudHMs
IGV2ZW50X3JlZ3M7DQo+ID4+ICsJdTMyIGNtZFtQVU5JVF9QQVJBTV9MRU5dID0gezB9Ow0KPiA+
PiAgIAlpbnQgcmV0Ow0KPiA+Pg0KPiA+PiAgIAlyZXQgPSBpbnRlbF9wbWNfaXBjX2NvbW1hbmQo
UE1DX0lQQ19QTUNfVEVMRU1UUlksDQo+ID4+IElPU1NfVEVMRU1fSU5GT19SRUFELCBAQCAtNjI2
LDggKzYzMiw5IEBAIHN0YXRpYyBpbnQNCj4gPj4gdGVsZW1ldHJ5X3NldHVwKHN0cnVjdCBwbGF0
Zm9ybV9kZXZpY2UgKnBkZXYpDQo+ID4+ICAgCXRlbG1fY29uZi0+aW9zc19jb25maWcubWF4X3Bl
cmlvZCA9DQo+IFRFTEVNX01BWF9QRVJJT0QocmVhZF9idWYpOw0KPiA+Pg0KPiA+PiAgIAkvKiBQ
VU5JVCBNYWlsYm94IFNldHVwICovDQo+ID4+IC0JcmV0ID0gaW50ZWxfcHVuaXRfaXBjX2NvbW1h
bmQoSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0lORk8sDQo+ID4+IDAsIDAsDQo+ID4+IC0JCQkJ
ICAgICAgTlVMTCwgJnJlYWRfYnVmKTsNCj4gPj4gKwlwdW5pdF9jbWRfaW5pdChjbWQsIElQQ19Q
VU5JVF9CSU9TX1JFQURfVEVMRV9JTkZPLCAwLCAwKTsNCj4gPj4gKwlyZXQgPSBpcGNfZGV2X3Jh
d19jbWQocHVuaXRfYmlvc19pcGNfZGV2LCBjbWQsDQo+ID4+IFBVTklUX1BBUkFNX0xFTiwNCj4g
Pj4gKwkJCU5VTEwsIDAsICZyZWFkX2J1ZiwgMSwgMCwgMCk7DQo+ID4+ICAgCWlmIChyZXQpIHsN
Cj4gPj4gICAJCWRldl9lcnIoJnBkZXYtPmRldiwgIlBTUyBURUxFTV9JTkZPIFJlYWQgRmFpbGVk
XG4iKTsNCj4gPj4gICAJCXJldHVybiByZXQ7DQo+ID4+IEBAIC02OTUsNiArNzAyLDcgQEAgc3Rh
dGljIGludCB0ZWxlbWV0cnlfcGx0X3NldF9zYW1wbGluZ19wZXJpb2QodTgNCj4gPj4gcHNzX3Bl
cmlvZCwgdTggaW9zc19wZXJpb2QpICB7DQo+ID4+ICAgCXUzMiB0ZWxlbV9jdHJsID0gMDsNCj4g
Pj4gICAJaW50IHJldCA9IDA7DQo+ID4+ICsJdTMyIGNtZFtQVU5JVF9QQVJBTV9MRU5dID0gezB9
Ow0KPiA+Pg0KPiA+PiAgIAltdXRleF9sb2NrKCYodGVsbV9jb25mLT50ZWxlbV9sb2NrKSk7DQo+
ID4+ICAgCWlmIChpb3NzX3BlcmlvZCkgew0KPiA+PiBAQCAtNzUyLDkgKzc2MCw5IEBAIHN0YXRp
YyBpbnQgdGVsZW1ldHJ5X3BsdF9zZXRfc2FtcGxpbmdfcGVyaW9kKHU4DQo+ID4+IHBzc19wZXJp
b2QsIHU4IGlvc3NfcGVyaW9kKQ0KPiA+PiAgIAkJfQ0KPiA+Pg0KPiA+PiAgIAkJLyogR2V0IHRl
bGVtZXRyeSBFVkVOVCBDVEwgKi8NCj4gPj4gLQkJcmV0ID0gaW50ZWxfcHVuaXRfaXBjX2NvbW1h
bmQoDQo+ID4+IC0JCQkJSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX0VWRU5UX0NUUkwsDQo+ID4+
IC0JCQkJMCwgMCwgTlVMTCwgJnRlbGVtX2N0cmwpOw0KPiA+PiArCQlwdW5pdF9jbWRfaW5pdChj
bWQsDQo+ID4+IElQQ19QVU5JVF9CSU9TX1JFQURfVEVMRV9FVkVOVF9DVFJMLCAwLCAwKTsNCj4g
Pj4gKwkJcmV0ID0gaXBjX2Rldl9yYXdfY21kKHB1bml0X2Jpb3NfaXBjX2RldiwgY21kLA0KPiA+
PiBQVU5JVF9QQVJBTV9MRU4sDQo+ID4+ICsJCQkJTlVMTCwgMCwgJnRlbGVtX2N0cmwsIDEsIDAs
IDApOw0KPiA+PiAgIAkJaWYgKHJldCkgew0KPiA+PiAgIAkJCXByX2VycigiUFNTIFRFTEVNX0NU
UkwgUmVhZCBGYWlsZWRcbiIpOw0KPiA+PiAgIAkJCWdvdG8gb3V0Ow0KPiA+PiBAQCAtNzYyLDkg
Kzc3MCwxMSBAQCBzdGF0aWMgaW50IHRlbGVtZXRyeV9wbHRfc2V0X3NhbXBsaW5nX3BlcmlvZCh1
OA0KPiA+PiBwc3NfcGVyaW9kLCB1OCBpb3NzX3BlcmlvZCkNCj4gPj4NCj4gPj4gICAJCS8qIERp
c2FibGUgVGVsZW1ldHJ5ICovDQo+ID4+ICAgCQlURUxFTV9ESVNBQkxFKHRlbGVtX2N0cmwpOw0K
PiA+PiAtCQlyZXQgPSBpbnRlbF9wdW5pdF9pcGNfY29tbWFuZCgNCj4gPj4gLQkJCQlJUENfUFVO
SVRfQklPU19XUklURV9URUxFX0VWRU5UX0NUUkwsDQo+ID4+IC0JCQkJMCwgMCwgJnRlbGVtX2N0
cmwsIE5VTEwpOw0KPiA+PiArCQlwdW5pdF9jbWRfaW5pdChjbWQsDQo+ID4+IElQQ19QVU5JVF9C
SU9TX1dSSVRFX1RFTEVfRVZFTlRfQ1RSTCwgMCwNCj4gPj4gKwkJCQkwKTsNCj4gPj4gKwkJcmV0
ID0gaXBjX2Rldl9yYXdfY21kKHB1bml0X2Jpb3NfaXBjX2RldiwgY21kLA0KPiA+PiBQVU5JVF9Q
QVJBTV9MRU4sDQo+ID4+ICsJCQkJKHU4ICopJnRlbGVtX2N0cmwsIHNpemVvZih0ZWxlbV9jdHJs
KSwgTlVMTCwNCj4gPj4gKwkJCQkwLCAwLCAwKTsNCj4gPj4gICAJCWlmIChyZXQpIHsNCj4gPj4g
ICAJCQlwcl9lcnIoIlBTUyBURUxFTV9DVFJMIEV2ZW50IERpc2FibGUgV3JpdGUNCj4gRmFpbGVk
XG4iKTsNCj4gPj4gICAJCQlnb3RvIG91dDsNCj4gPj4gQEAgLTc3Niw5ICs3ODYsMTEgQEAgc3Rh
dGljIGludCB0ZWxlbWV0cnlfcGx0X3NldF9zYW1wbGluZ19wZXJpb2QodTgNCj4gPj4gcHNzX3Bl
cmlvZCwgdTggaW9zc19wZXJpb2QpDQo+ID4+ICAgCQlURUxFTV9FTkFCTEVfUEVSSU9ESUModGVs
ZW1fY3RybCk7DQo+ID4+ICAgCQl0ZWxlbV9jdHJsIHw9IHBzc19wZXJpb2Q7DQo+ID4+DQo+ID4+
IC0JCXJldCA9IGludGVsX3B1bml0X2lwY19jb21tYW5kKA0KPiA+PiAtCQkJCUlQQ19QVU5JVF9C
SU9TX1dSSVRFX1RFTEVfRVZFTlRfQ1RSTCwNCj4gPj4gLQkJCQkwLCAwLCAmdGVsZW1fY3RybCwg
TlVMTCk7DQo+ID4+ICsJCXB1bml0X2NtZF9pbml0KGNtZCwNCj4gPj4gSVBDX1BVTklUX0JJT1Nf
V1JJVEVfVEVMRV9FVkVOVF9DVFJMLCAwLA0KPiA+PiArCQkJCTApOw0KPiA+PiArCQlyZXQgPSBp
cGNfZGV2X3Jhd19jbWQocHVuaXRfYmlvc19pcGNfZGV2LCBjbWQsDQo+ID4+IFBVTklUX1BBUkFN
X0xFTiwNCj4gPj4gKwkJCQkodTggKikmdGVsZW1fY3RybCwgc2l6ZW9mKHRlbGVtX2N0cmwpLCBO
VUxMLA0KPiA+PiArCQkJCTAsIDAsIDApOw0KPiA+PiAgIAkJaWYgKHJldCkgew0KPiA+PiAgIAkJ
CXByX2VycigiUFNTIFRFTEVNX0NUUkwgRXZlbnQgRW5hYmxlIFdyaXRlDQo+IEZhaWxlZFxuIik7
DQo+ID4+ICAgCQkJZ290byBvdXQ7DQo+ID4+IEBAIC0xMDEzLDYgKzEwMjUsNyBAQCBzdGF0aWMg
aW50DQo+ID4+IHRlbGVtZXRyeV9wbHRfZ2V0X3RyYWNlX3ZlcmJvc2l0eShlbnVtDQo+ID4+IHRl
bGVtZXRyeV91bml0IHRlbGVtX3VuaXQsICB7DQo+ID4+ICAgCXUzMiB0ZW1wID0gMDsNCj4gPj4g
ICAJaW50IHJldDsNCj4gPj4gKwl1MzIgY21kW1BVTklUX1BBUkFNX0xFTl0gPSB7MH07DQo+ID4+
DQo+ID4+ICAgCWlmICh2ZXJib3NpdHkgPT0gTlVMTCkNCj4gPj4gICAJCXJldHVybiAtRUlOVkFM
Ow0KPiA+PiBAQCAtMTAyMCw5ICsxMDMzLDkgQEAgc3RhdGljIGludA0KPiA+PiB0ZWxlbWV0cnlf
cGx0X2dldF90cmFjZV92ZXJib3NpdHkoZW51bQ0KPiA+PiB0ZWxlbWV0cnlfdW5pdCB0ZWxlbV91
bml0LA0KPiA+PiAgIAltdXRleF9sb2NrKCYodGVsbV9jb25mLT50ZWxlbV90cmFjZV9sb2NrKSk7
DQo+ID4+ICAgCXN3aXRjaCAodGVsZW1fdW5pdCkgew0KPiA+PiAgIAljYXNlIFRFTEVNX1BTUzoN
Cj4gPj4gLQkJcmV0ID0gaW50ZWxfcHVuaXRfaXBjX2NvbW1hbmQoDQo+ID4+IC0JCQkJSVBDX1BV
TklUX0JJT1NfUkVBRF9URUxFX1RSQUNFX0NUUkwsDQo+ID4+IC0JCQkJMCwgMCwgTlVMTCwgJnRl
bXApOw0KPiA+PiArCQlwdW5pdF9jbWRfaW5pdChjbWQsDQo+ID4+IElQQ19QVU5JVF9CSU9TX1JF
QURfVEVMRV9UUkFDRV9DVFJMLCAwLCAwKTsNCj4gPj4gKwkJcmV0ID0gaXBjX2Rldl9yYXdfY21k
KHB1bml0X2Jpb3NfaXBjX2RldiwgY21kLA0KPiA+PiBQVU5JVF9QQVJBTV9MRU4sDQo+ID4+ICsJ
CQkJTlVMTCwgMCwgJnRlbXAsIDEsIDAsIDApOw0KPiA+PiAgIAkJaWYgKHJldCkgew0KPiA+PiAg
IAkJCXByX2VycigiUFNTIFRSQUNFX0NUUkwgUmVhZCBGYWlsZWRcbiIpOw0KPiA+PiAgIAkJCWdv
dG8gb3V0Ow0KPiA+PiBAQCAtMTA1OCwxNSArMTA3MSwxNiBAQCBzdGF0aWMgaW50DQo+ID4+IHRl
bGVtZXRyeV9wbHRfc2V0X3RyYWNlX3ZlcmJvc2l0eShlbnVtIHRlbGVtZXRyeV91bml0IHRlbGVt
X3VuaXQsICB7DQo+ID4+ICAgCXUzMiB0ZW1wID0gMDsNCj4gPj4gICAJaW50IHJldDsNCj4gPj4g
Kwl1MzIgY21kW1BVTklUX1BBUkFNX0xFTl0gPSB7MH07DQo+ID4+DQo+ID4+ICAgCXZlcmJvc2l0
eSAmPSBURUxFTV9UUkNfVkVSQk9TSVRZX01BU0s7DQo+ID4+DQo+ID4+ICAgCW11dGV4X2xvY2so
Jih0ZWxtX2NvbmYtPnRlbGVtX3RyYWNlX2xvY2spKTsNCj4gPj4gICAJc3dpdGNoICh0ZWxlbV91
bml0KSB7DQo+ID4+ICAgCWNhc2UgVEVMRU1fUFNTOg0KPiA+PiAtCQlyZXQgPSBpbnRlbF9wdW5p
dF9pcGNfY29tbWFuZCgNCj4gPj4gLQkJCQlJUENfUFVOSVRfQklPU19SRUFEX1RFTEVfVFJBQ0Vf
Q1RSTCwNCj4gPj4gLQkJCQkwLCAwLCBOVUxMLCAmdGVtcCk7DQo+ID4+ICsJCXB1bml0X2NtZF9p
bml0KGNtZCwNCj4gPj4gSVBDX1BVTklUX0JJT1NfUkVBRF9URUxFX1RSQUNFX0NUUkwsIDAsIDAp
Ow0KPiA+PiArCQlyZXQgPSBpcGNfZGV2X3Jhd19jbWQocHVuaXRfYmlvc19pcGNfZGV2LCBjbWQs
DQo+ID4+IFBVTklUX1BBUkFNX0xFTiwNCj4gPj4gKwkJCQlOVUxMLCAwLCAmdGVtcCwgMSwgMCwg
MCk7DQo+ID4+ICAgCQlpZiAocmV0KSB7DQo+ID4+ICAgCQkJcHJfZXJyKCJQU1MgVFJBQ0VfQ1RS
TCBSZWFkIEZhaWxlZFxuIik7DQo+ID4+ICAgCQkJZ290byBvdXQ7DQo+ID4+IEBAIC0xMDc0LDEw
ICsxMDg4LDEwIEBAIHN0YXRpYyBpbnQNCj4gPj4gdGVsZW1ldHJ5X3BsdF9zZXRfdHJhY2VfdmVy
Ym9zaXR5KGVudW0gdGVsZW1ldHJ5X3VuaXQgdGVsZW1fdW5pdCwNCj4gPj4NCj4gPj4gICAJCVRF
TEVNX0NMRUFSX1ZFUkJPU0lUWV9CSVRTKHRlbXApOw0KPiA+PiAgIAkJVEVMRU1fU0VUX1ZFUkJP
U0lUWV9CSVRTKHRlbXAsIHZlcmJvc2l0eSk7DQo+ID4+IC0NCj4gPj4gLQkJcmV0ID0gaW50ZWxf
cHVuaXRfaXBjX2NvbW1hbmQoDQo+ID4+IC0JCQkJSVBDX1BVTklUX0JJT1NfV1JJVEVfVEVMRV9U
UkFDRV9DVFJMLA0KPiA+PiAtCQkJCTAsIDAsICZ0ZW1wLCBOVUxMKTsNCj4gPj4gKwkJcHVuaXRf
Y21kX2luaXQoY21kLA0KPiA+PiBJUENfUFVOSVRfQklPU19XUklURV9URUxFX0VWRU5UX0NUUkws
DQo+ID4+ICsJCQkJMCwgMCk7DQo+ID4+ICsJCXJldCA9IGlwY19kZXZfcmF3X2NtZChwdW5pdF9i
aW9zX2lwY19kZXYsIGNtZCwNCj4gPj4gUFVOSVRfUEFSQU1fTEVOLA0KPiA+PiArCQkJCSh1OCAq
KSZ0ZW1wLCBzaXplb2YodGVtcCksIE5VTEwsIDAsIDAsIDApOw0KPiA+PiAgIAkJaWYgKHJldCkg
ew0KPiA+PiAgIAkJCXByX2VycigiUFNTIFRSQUNFX0NUUkwgVmVyYm9zaXR5IFNldCBGYWlsZWRc
biIpOw0KPiA+PiAgIAkJCWdvdG8gb3V0Ow0KPiA+PiBAQCAtMTEzOSw2ICsxMTUzLDEwIEBAIHN0
YXRpYyBpbnQgdGVsZW1ldHJ5X3BsdGRydl9wcm9iZShzdHJ1Y3QNCj4gPj4gcGxhdGZvcm1fZGV2
aWNlICpwZGV2KQ0KPiA+PiAgIAlpZiAoIWlkKQ0KPiA+PiAgIAkJcmV0dXJuIC1FTk9ERVY7DQo+
ID4+DQo+ID4+ICsJcHVuaXRfYmlvc19pcGNfZGV2ID0gaW50ZWxfaXBjX2Rldl9nZXQoUFVOSVRf
QklPU19JUENfREVWKTsNCj4gPj4gKwlpZiAoSVNfRVJSX09SX05VTEwocHVuaXRfYmlvc19pcGNf
ZGV2KSkNCj4gPj4gKwkJcmV0dXJuIFBUUl9FUlIocHVuaXRfYmlvc19pcGNfZGV2KTsNCj4gPj4g
Kw0KPiA+PiAgIAl0ZWxtX2NvbmYgPSAoc3RydWN0IHRlbGVtZXRyeV9wbHRfY29uZmlnICopaWQt
PmRyaXZlcl9kYXRhOw0KPiA+Pg0KPiA+PiAgIAlyZXMwID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNl
KHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAwKTsgQEAgLQ0KPiA+PiAxMjE4LDYgKzEyMzYsNyBAQCBz
dGF0aWMgaW50IHRlbGVtZXRyeV9wbHRkcnZfcHJvYmUoc3RydWN0DQo+ID4+IHBsYXRmb3JtX2Rl
dmljZQ0KPiA+PiAqcGRldikgIHN0YXRpYyBpbnQgdGVsZW1ldHJ5X3BsdGRydl9yZW1vdmUoc3Ry
dWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikNCj4gew0KPiA+PiAgIAl0ZWxlbWV0cnlfY2xlYXJf
cGx0ZGF0YSgpOw0KPiA+PiArCWludGVsX2lwY19kZXZfcHV0KHB1bml0X2Jpb3NfaXBjX2Rldik7
DQo+ID4+ICAgCWlvdW5tYXAodGVsbV9jb25mLT5wc3NfY29uZmlnLnJlZ21hcCk7DQo+ID4+ICAg
CWlvdW5tYXAodGVsbV9jb25mLT5pb3NzX2NvbmZpZy5yZWdtYXApOw0KPiA+Pg0KPiA+PiAtLQ0K
PiA+PiAyLjcuNA0KPiA+DQo+IA0KPiAtLQ0KPiBTYXRoeWFuYXJheWFuYW4gS3VwcHVzd2FteQ0K
PiBMaW51eCBrZXJuZWwgZGV2ZWxvcGVyDQoNCg==

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls
@ 2017-10-11  3:32         ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:32 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid



> -----Original Message-----
> From: platform-driver-x86-owner@vger.kernel.org [mailto:platform-driver-
> x86-owner@vger.kernel.org] On Behalf Of sathyanarayanan kuppuswamy
> Sent: Wednesday, October 11, 2017 3:59 AM
> To: Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>;
> a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org
> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> sathyaosid@gmail.com
> Subject: Re: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc
> device calls
> 
> 
> 
> On 10/08/2017 10:07 PM, Chakravarty, Souvik K wrote:
> >> From: sathyanarayanan.kuppuswamy@linux.intel.com
> >> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> >> Sent: Sunday, October 8, 2017 3:50 AM
> >> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> >> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> >> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> >> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org;
> >> Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>
> >> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> >> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> >> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> Subject: [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic
> >> intel ipc device calls
> >>
> >> From: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >>
> >> Removed redundant IPC helper functions and refactored the driver to
> >> use APIs provided by generic IPC driver. This patch also cleans-up
> >> PUNIT IPC user
> >> drivers(intel_telemetry_pltdrv.c) to use APIs provided by generic IPC
> driver.
> >>
> >> Signed-off-by: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> ---
> >>   arch/x86/include/asm/intel_punit_ipc.h        | 125 +++++------
> >>   drivers/platform/x86/Kconfig                  |   1 +
> >>   drivers/platform/x86/intel_punit_ipc.c        | 303 ++++++++++----------------
> >>   drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +++++----
> >>   4 files changed, 223 insertions(+), 303 deletions(-)
> >>
> >> Changes since v4:
> >>   * None
> >>
> >> Changes since v2:
> >>   * Added unique name to PUNIT BIOS, GTD, & ISP regmaps.
> >>   * Added intel_ipc_dev_put() support.
> >>
> >> Changes since v1:
> >>   * Removed custom APIs.
> >>   * Cleaned up PUNIT IPC user drivers to use APIs provided by generic
> >>     IPC driver.
> >>
> >> diff --git a/arch/x86/include/asm/intel_punit_ipc.h
> >> b/arch/x86/include/asm/intel_punit_ipc.h
> >> index 201eb9d..cf1630c 100644
> >> --- a/arch/x86/include/asm/intel_punit_ipc.h
> >> +++ b/arch/x86/include/asm/intel_punit_ipc.h
> >> @@ -1,10 +1,8 @@
> >>   #ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
> >>   #define  _ASM_X86_INTEL_PUNIT_IPC_H_
> >>
> >> -/*
> >> - * Three types of 8bit P-Unit IPC commands are supported,
> >> - * bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
> >> - */
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +
> >>   typedef enum {
> >>   	BIOS_IPC = 0,
> >>   	GTDRIVER_IPC,
> >> @@ -12,61 +10,60 @@ typedef enum {
> >>   	RESERVED_IPC,
> >>   } IPC_TYPE;
> >>
> >> -#define IPC_TYPE_OFFSET			6
> >> -#define IPC_PUNIT_BIOS_CMD_BASE		(BIOS_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_GTD_CMD_BASE		(GTDDRIVER_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_ISPD_CMD_BASE		(ISPDRIVER_IPC <<
> >> IPC_TYPE_OFFSET)
> >> -#define IPC_PUNIT_CMD_TYPE_MASK		(RESERVED_IPC <<
> >> IPC_TYPE_OFFSET)
> >> +#define PUNIT_BIOS_IPC_DEV			"punit_bios_ipc"
> >> +#define PUNIT_GTD_IPC_DEV			"punit_gtd_ipc"
> >> +#define PUNIT_ISP_IPC_DEV			"punit_isp_ipc"
> >> +#define PUNIT_PARAM_LEN				3
> >>
> >>   /* BIOS => Pcode commands */
> >> -#define IPC_PUNIT_BIOS_ZERO
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_BIOS_VR_INTERFACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_BIOS_READ_PCS
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_BIOS_WRITE_PCS
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x03)
> >> -#define IPC_PUNIT_BIOS_READ_PCU_CONFIG
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x04)
> >> -#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x05)
> >> -#define IPC_PUNIT_BIOS_READ_PL1_SETTING
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x06)
> >> -#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING
> 	(IPC_PUNIT_BIOS_CMD_BASE
> >> | 0x07)
> >> -#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x08)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_INFO
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x09)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_TRACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
> >> -#define IPC_PUNIT_BIOS_READ_TELE_EVENT
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x10)
> >> -#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x11)
> >> -#define IPC_PUNIT_BIOS_READ_MODULE_TEMP
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x12)
> >> -#define IPC_PUNIT_BIOS_RESERVED
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x13)
> >> -#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x14)
> >> -#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x15)
> >> -#define IPC_PUNIT_BIOS_READ_RATIO_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x16)
> >> -#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x17)
> >> -#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x18)
> >> -#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x19)
> >> -#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
> >> -#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH
> >> 	(IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
> >> +#define IPC_PUNIT_BIOS_ZERO			(0x00)
> >> +#define IPC_PUNIT_BIOS_VR_INTERFACE		(0x01)
> >> +#define IPC_PUNIT_BIOS_READ_PCS			(0x02)
> >> +#define IPC_PUNIT_BIOS_WRITE_PCS		(0x03)
> >> +#define IPC_PUNIT_BIOS_READ_PCU_CONFIG		(0x04)
> >> +#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG		(0x05)
> >> +#define IPC_PUNIT_BIOS_READ_PL1_SETTING		(0x06)
> >> +#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING	(0x07)
> >> +#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM		(0x08)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_INFO		(0x09)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL	(0x0a)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL	(0x0b)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL	(0x0c)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL	(0x0d)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_TRACE		(0x0e)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE		(0x0f)
> >> +#define IPC_PUNIT_BIOS_READ_TELE_EVENT		(0x10)
> >> +#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT		(0x11)
> >> +#define IPC_PUNIT_BIOS_READ_MODULE_TEMP		(0x12)
> >> +#define IPC_PUNIT_BIOS_RESERVED			(0x13)
> >> +#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER	(0x14)
> >> +#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER	(0x15)
> >> +#define IPC_PUNIT_BIOS_READ_RATIO_OVER		(0x16)
> >> +#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER		(0x17)
> >> +#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL		(0x18)
> >> +#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL		(0x19)
> >> +#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH	(0x1a)
> >> +#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH	(0x1b)
> >>
> >>   /* GT Driver => Pcode commands */
> >> -#define IPC_PUNIT_GTD_ZERO
> 	(IPC_PUNIT_GTD_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_GTD_CONFIG
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x03)
> >> -#define IPC_PUNIT_GTD_GET_WM_VAL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x06)
> >> -#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x07)
> >> -#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x16)
> >> -#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x17)
> >> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1a)
> >> -#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING
> >> 	(IPC_PUNIT_GTD_CMD_BASE | 0x1c)
> >> +#define IPC_PUNIT_GTD_ZERO			(0x00)
> >> +#define IPC_PUNIT_GTD_CONFIG			(0x01)
> >> +#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL	(0x02)
> >> +#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL	(0x03)
> >> +#define IPC_PUNIT_GTD_GET_WM_VAL		(0x06)
> >> +#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ	(0x07)
> >> +#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE	(0x16)
> >> +#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST	(0x17)
> >> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL	(0x1a)
> >> +#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING	(0x1c)
> >>
> >>   /* ISP Driver => Pcode commands */
> >> -#define IPC_PUNIT_ISPD_ZERO
> 	(IPC_PUNIT_ISPD_CMD_BASE
> >> | 0x00)
> >> -#define IPC_PUNIT_ISPD_CONFIG
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x01)
> >> -#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x02)
> >> -#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x03)
> >> -#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x04)
> >> -#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL
> >> 	(IPC_PUNIT_ISPD_CMD_BASE | 0x05)
> >> +#define IPC_PUNIT_ISPD_ZERO			(0x00)
> >> +#define IPC_PUNIT_ISPD_CONFIG			(0x01)
> >> +#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL		(0x02)
> >> +#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS	(0x03)
> >> +#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL		(0x04)
> >> +#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL		(0x05)
> >>
> >>   /* Error codes */
> >>   #define IPC_PUNIT_ERR_SUCCESS			0
> >> @@ -77,25 +74,11 @@ typedef enum {
> >>   #define IPC_PUNIT_ERR_INVALID_VR_ID		5
> >>   #define IPC_PUNIT_ERR_VR_ERR			6
> >>
> >> -#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> >> -
> >> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
> >> -int
> >> intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32
> >> *out);
> >> -
> >> -#else
> >> -
> >> -static inline int intel_punit_ipc_simple_command(int cmd,
> >> -						  int para1, int para2)
> >> +static inline void punit_cmd_init(u32 *cmd, u32 param1, u32 param2,
> >> +u32
> >> +param3)
> >>   {
> >> -	return -ENODEV;
> >> +	cmd[0] = param1;
> >> +	cmd[1] = param2;
> >> +	cmd[2] = param3;
> >>   }
> >>
> >> -static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32
> para2,
> >> -					  u32 *in, u32 *out)
> >> -{
> >> -	return -ENODEV;
> >> -}
> >> -
> >> -#endif /* CONFIG_INTEL_PUNIT_IPC */
> >> -
> >>   #endif
> >> diff --git a/drivers/platform/x86/Kconfig
> >> b/drivers/platform/x86/Kconfig index 724ee696..9442c23 100644
> >> --- a/drivers/platform/x86/Kconfig
> >> +++ b/drivers/platform/x86/Kconfig
> >> @@ -1096,6 +1096,7 @@ config SURFACE_3_BUTTON
> >>
> >>   config INTEL_PUNIT_IPC
> >>   	tristate "Intel P-Unit IPC Driver"
> >> +	select REGMAP_MMIO
> >>   	---help---
> >>   	  This driver provides support for Intel P-Unit Mailbox IPC
> >> mechanism,
> >>   	  which is used to bridge the communications between kernel and P-
> >> Unit.
> >> diff --git a/drivers/platform/x86/intel_punit_ipc.c
> >> b/drivers/platform/x86/intel_punit_ipc.c
> >> index b5b8901..f310a05 100644
> >> --- a/drivers/platform/x86/intel_punit_ipc.c
> >> +++ b/drivers/platform/x86/intel_punit_ipc.c
> >> @@ -18,18 +18,18 @@
> >>   #include <linux/device.h>
> >>   #include <linux/interrupt.h>
> >>   #include <linux/platform_device.h>
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +#include <linux/regmap.h>
> >>   #include <asm/intel_punit_ipc.h>
> >>
> >> -/* IPC Mailbox registers */
> >> -#define OFFSET_DATA_LOW		0x0
> >> -#define OFFSET_DATA_HIGH	0x4
> >>   /* bit field of interface register */
> >>   #define	CMD_RUN			BIT(31)
> >> -#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
> >> +#define CMD_ERRCODE_MASK	GENMASK(7, 0)
> >>   #define	CMD_PARA1_SHIFT		8
> >>   #define	CMD_PARA2_SHIFT		16
> >>
> >> -#define CMD_TIMEOUT_SECONDS	1
> >> +/* IPC PUNIT commands */
> >> +#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7,
> >> 0)
> >>
> >>   enum {
> >>   	BASE_DATA = 0,
> >> @@ -39,187 +39,42 @@ enum {
> >>
> >>   typedef struct {
> >>   	struct device *dev;
> >> -	struct mutex lock;
> >> -	int irq;
> >> -	struct completion cmd_complete;
> >>   	/* base of interface and data registers */
> >>   	void __iomem *base[RESERVED_IPC][BASE_MAX];
> >> +	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
> >>   	IPC_TYPE type;
> >>   } IPC_DEV;
> >>
> >>   static IPC_DEV *punit_ipcdev;
> >>
> >> -static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_IFACE]);
> >> -}
> >> -
> >> -static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32
> >> cmd) - {
> >> -	writel(cmd, ipcdev->base[type][BASE_IFACE]);
> >> -}
> >> -
> >> -static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> >> -}
> >> -
> >> -static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type) -{
> >> -	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> >> -}
> >> -
> >> -static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE
> >> type, u32
> >> data) -{
> >> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
> >> -}
> >> -
> >> -static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE
> >> type, u32
> >> data) -{
> >> -	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
> >> -}
> >> +const char *ipc_dev_name[RESERVED_IPC] = {
> >> +	PUNIT_BIOS_IPC_DEV,
> >> +	PUNIT_GTD_IPC_DEV,
> >> +	PUNIT_ISP_IPC_DEV
> >> +};
> >>
> >> -static const char *ipc_err_string(int error) -{
> >> -	if (error == IPC_PUNIT_ERR_SUCCESS)
> >> -		return "no error";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
> >> -		return "invalid command";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
> >> -		return "invalid parameter";
> >> -	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
> >> -		return "command timeout";
> >> -	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
> >> -		return "command locked";
> >> -	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
> >> -		return "invalid vr id";
> >> -	else if (error == IPC_PUNIT_ERR_VR_ERR)
> >> -		return "vr error";
> >> -	else
> >> -		return "unknown error";
> >> -}
> >> +static struct regmap_config punit_regmap_config = {
> >> +        .reg_bits = 32,
> >> +        .reg_stride = 4,
> >> +        .val_bits = 32,
> >> +};
> >>
> >> -static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE
> >> type)
> >> +int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
> >>   {
> >> -	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> >> -	int errcode;
> >> -	int status;
> >> -
> >> -	if (ipcdev->irq) {
> >> -		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
> >> -						 CMD_TIMEOUT_SECONDS *
> >> HZ)) {
> >> -			dev_err(ipcdev->dev, "IPC timed out\n");
> >> -			return -ETIMEDOUT;
> >> -		}
> >> -	} else {
> >> -		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --
> >> loops)
> >> -			udelay(1);
> >> -		if (!loops) {
> >> -			dev_err(ipcdev->dev, "IPC timed out\n");
> >> -			return -ETIMEDOUT;
> >> -		}
> >> -	}
> >> +	if (!cmd_list || cmdlen != PUNIT_PARAM_LEN)
> >> +		return -EINVAL;
> >>
> >> -	status = ipc_read_status(ipcdev, type);
> >> -	errcode = status & CMD_ERRCODE_MASK;
> >> -	if (errcode) {
> >> -		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
> >> -			ipc_err_string(errcode), status);
> >> -		return -EIO;
> >> -	}
> >> +	cmd_list[0] |= CMD_RUN | cmd_list[1] << CMD_PARA1_SHIFT |
> >> +		cmd_list[2] << CMD_PARA1_SHIFT;
> >>
> >>   	return 0;
> >>   }
> >>
> >> -/**
> >> - * intel_punit_ipc_simple_command() - Simple IPC command
> >> - * @cmd:	IPC command code.
> >> - * @para1:	First 8bit parameter, set 0 if not used.
> >> - * @para2:	Second 8bit parameter, set 0 if not used.
> >> - *
> >> - * Send a IPC command to P-Unit when there is no data transaction
> >> - *
> >> - * Return:	IPC error code or 0 on success.
> >> - */
> >> -int intel_punit_ipc_simple_command(int cmd, int para1, int para2) -{
> >> -	IPC_DEV *ipcdev = punit_ipcdev;
> >> -	IPC_TYPE type;
> >> -	u32 val;
> >> -	int ret;
> >> -
> >> -	mutex_lock(&ipcdev->lock);
> >> -
> >> -	reinit_completion(&ipcdev->cmd_complete);
> >> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> >> -
> >> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> >> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> >> CMD_PARA1_SHIFT;
> >> -	ipc_write_cmd(ipcdev, type, val);
> >> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> >> -
> >> -	mutex_unlock(&ipcdev->lock);
> >> -
> >> -	return ret;
> >> -}
> >> -EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> >> -
> >> -/**
> >> - * intel_punit_ipc_command() - IPC command with data and pointers
> >> - * @cmd:	IPC command code.
> >> - * @para1:	First 8bit parameter, set 0 if not used.
> >> - * @para2:	Second 8bit parameter, set 0 if not used.
> >> - * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD
> >> and ISPD.
> >> - * @out:	Output data.
> >> - *
> >> - * Send a IPC command to P-Unit with data transaction
> >> - *
> >> - * Return:	IPC error code or 0 on success.
> >> - */
> >> -int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in,
> >> u32
> >> *out) -{
> >> -	IPC_DEV *ipcdev = punit_ipcdev;
> >> -	IPC_TYPE type;
> >> -	u32 val;
> >> -	int ret;
> >> -
> >> -	mutex_lock(&ipcdev->lock);
> >> -
> >> -	reinit_completion(&ipcdev->cmd_complete);
> >> -	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
> >> -
> >> -	if (in) {
> >> -		ipc_write_data_low(ipcdev, type, *in);
> >> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> >> -			ipc_write_data_high(ipcdev, type, *++in);
> >> -	}
> >> -
> >> -	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
> >> -	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 <<
> >> CMD_PARA1_SHIFT;
> >> -	ipc_write_cmd(ipcdev, type, val);
> >> -
> >> -	ret = intel_punit_ipc_check_status(ipcdev, type);
> >> -	if (ret)
> >> -		goto out;
> >> -
> >> -	if (out) {
> >> -		*out = ipc_read_data_low(ipcdev, type);
> >> -		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
> >> -			*++out = ipc_read_data_high(ipcdev, type);
> >> -	}
> >> -
> >> -out:
> >> -	mutex_unlock(&ipcdev->lock);
> >> -	return ret;
> >> -}
> >> -EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> >> -
> >> -static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> >> +/* Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD. */
> >> +int
> >> +pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen, u32 *out,
> >> +		u32 outlen, u32 dptr, u32 sptr)
> >>   {
> >> -	IPC_DEV *ipcdev = dev_id;
> >> -
> >> -	complete(&ipcdev->cmd_complete);
> >> -	return IRQ_HANDLED;
> >> +	return pre_simple_cmd_fn(cmd_list, cmdlen);
> >>   }
> >>
> >>   static int intel_punit_get_bars(struct platform_device *pdev) @@
> >> -282,9
> >> +137,77 @@ static int intel_punit_get_bars(struct platform_device
> >> +*pdev)
> >>   	return 0;
> >>   }
> >>
> >> +static int punit_ipc_err_code(int status) {
> >> +	return (status & CMD_ERRCODE_MASK); }
> >> +
> >> +static int punit_ipc_busy_check(int status) {
> >> +	return status | CMD_RUN;
> >> +}
> >> +
> >> +static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device
> *dev,
> >> +		const char *devname,
> >> +		int irq,
> >> +		void __iomem *base,
> >> +		void __iomem *data)
> >> +{
> >> +	struct intel_ipc_dev_ops *ops;
> >> +	struct intel_ipc_dev_cfg *cfg;
> >> +	struct regmap *cmd_regs, *data_regs;
> >> +
> >> +        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
> >> +        if (!cfg)
> >> +                return ERR_PTR(-ENOMEM);
> >> +
> >> +	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
> >> +	if (!ops)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> >> "%s_%s",
> >> +						  devname, "base");
> >> +
> >> +	cmd_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
> >> +			&punit_regmap_config);
> >> +	if (IS_ERR(cmd_regs)) {
> >> +                dev_err(dev, "cmd_regs regmap init failed\n");
> >> +                return ERR_CAST(cmd_regs);;
> >> +        }
> >> +
> >> +	punit_regmap_config.name = devm_kasprintf(dev, GFP_KERNEL,
> >> "%s_%s",
> >> +						  devname, "data");
> >> +
> >> +        data_regs = devm_regmap_init_mmio_clk(dev, NULL, data,
> >> +			&punit_regmap_config);
> >> +        if (IS_ERR(data_regs)) {
> >> +                dev_err(dev, "data_regs regmap init failed\n");
> >> +                return ERR_CAST(data_regs);;
> >> +        }
> >> +
> >> +	/* set IPC dev ops */
> >> +	ops->to_err_code = punit_ipc_err_code;
> >> +	ops->busy_check = punit_ipc_busy_check;
> >> +	ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
> >> +	ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
> >> +
> >> +	if (irq > 0)
> >> +	        cfg->mode = IPC_DEV_MODE_IRQ;
> >> +	else
> >> +	        cfg->mode = IPC_DEV_MODE_POLLING;
> >> +
> >> +	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
> >> +	cfg->irq = irq;
> >> +	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
> >> +	cfg->cmd_regs = cmd_regs;
> >> +	cfg->data_regs = data_regs;
> >> +
> >> +	return devm_intel_ipc_dev_create(dev, devname, cfg, ops); }
> >> +
> >>   static int intel_punit_ipc_probe(struct platform_device *pdev)  {
> >> -	int irq, ret;
> >> +	int irq, ret, i;
> >>
> >>   	punit_ipcdev = devm_kzalloc(&pdev->dev,
> >>   				    sizeof(*punit_ipcdev), GFP_KERNEL); @@ -
> >> 294,35 +217,30 @@ static int intel_punit_ipc_probe(struct
> >> platform_device
> >> *pdev)
> >>   	platform_set_drvdata(pdev, punit_ipcdev);
> >>
> >>   	irq = platform_get_irq(pdev, 0);
> >> -	if (irq < 0) {
> >> -		punit_ipcdev->irq = 0;
> >> -		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
> >> -	} else {
> >> -		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
> >> -				       IRQF_NO_SUSPEND, "intel_punit_ipc",
> >> -				       &punit_ipcdev);
> >> -		if (ret) {
> >> -			dev_err(&pdev->dev, "Failed to request irq: %d\n",
> >> irq);
> >> -			return ret;
> >> -		}
> >> -		punit_ipcdev->irq = irq;
> >> -	}
> >>
> >>   	ret = intel_punit_get_bars(pdev);
> >>   	if (ret)
> >> -		goto out;
> >> +		return ret;
> >> +
> >> +	for (i = 0; i < RESERVED_IPC; i++) {
> >> +		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
> >> +				&pdev->dev,
> >> +				ipc_dev_name[i],
> >> +				irq,
> >> +				punit_ipcdev->base[i][BASE_IFACE],
> >> +				punit_ipcdev->base[i][BASE_DATA]);
> >> +
> >> +		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
> >> +			dev_err(&pdev->dev, "%s create failed\n",
> >> +					ipc_dev_name[i]);
> >> +			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
> >> +		}
> >> +	}
> >>
> >>   	punit_ipcdev->dev = &pdev->dev;
> >> -	mutex_init(&punit_ipcdev->lock);
> >> -	init_completion(&punit_ipcdev->cmd_complete);
> >>
> >> -out:
> >>   	return ret;
> >> -}
> >>
> >> -static int intel_punit_ipc_remove(struct platform_device *pdev) -{
> >> -	return 0;
> >>   }
> >>
> >>   static const struct acpi_device_id punit_ipc_acpi_ids[] = { @@
> >> -332,7 +250,6 @@ static const struct acpi_device_id
> >> punit_ipc_acpi_ids[] = {
> >>
> >>   static struct platform_driver intel_punit_ipc_driver = {
> >>   	.probe = intel_punit_ipc_probe,
> >> -	.remove = intel_punit_ipc_remove,
> >>   	.driver = {
> >>   		.name = "intel_punit_ipc",
> >>   		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), diff --git
> >> a/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> b/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> index e0424d5..bf8284a 100644
> >> --- a/drivers/platform/x86/intel_telemetry_pltdrv.c
> >> +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
> > This is a logical separation and so should be a different patch.
> But if we split it into two different patches then it will break the bisect-
> ability of these patches.
> >
> >> @@ -98,6 +98,7 @@ struct telem_ssram_region {  };
> >>
> >>   static struct telemetry_plt_config *telm_conf;
> >> +static struct intel_ipc_dev *punit_bios_ipc_dev;
> > Simply punit_ipc_dev is good enough.
> There are three PUNIT IPC devices. So to differentiate between them I added
> extra string to it.

OK that makes sense. 
However punit_bios_ipc is neither the actual device type/name nor indicates the usage of it since BIOS has got nothing to do with it (except for exposing the SSRAM base addr).
Can we rename this to something that actually denotes what this is, e.g, "punit_telemetry_ipc_dev". Same change needs to be done in intel_punit_ipc.c as well. 

> >
> >>   /*
> >>    * The following counters are programmed by default during setup.
> >> @@ -127,7 +128,6 @@ static struct telemetry_evtmap
> >>   	{"PMC_S0IX_BLOCK_IPS_CLOCKS",           0x600B},
> >>   };
> >>
> >> -
> >>   static struct telemetry_evtmap
> >>
> >> 	telemetry_apl_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVE
> >> NTS] = {
> >>   	{"IA_CORE0_C6_RES",			0x0400},
> >> @@ -283,13 +283,12 @@ static inline int
> >> telemetry_plt_config_ioss_event(u32 evt_id, int index)  static inline
> >> int
> >> telemetry_plt_config_pss_event(u32 evt_id, int index)  {
> >>   	u32 write_buf;
> >> -	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	write_buf = evt_id | TELEM_EVENT_ENABLE;
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT,
> >> -				      index, 0, &write_buf, NULL);
> >> -
> >> -	return ret;
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT, index, 0);
> >> +	return ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&write_buf, sizeof(write_buf), NULL, 0, 0, 0);
> >>   }
> > 1) Why use raw_cmd here? It should use ipc_dev_cmd() according to
> original design. Same everywhere though this file.
> In my initial versions of this patch set, I had only exported
> ipc_dev_raw_cmd() API. Since this driver refactoring work was done during
> that time, I have used intel_dev_raw_cmd() API in it. I have added
> ipc_dev_cmd() API only recently  to handle some new requirements in
> intel_scu_ipc.c driver.
> 
> I will fix it in next version.
> 
> > 2) punit_cmd_init and ipc_dev_raw_cmd are repeated multiple time
> > thoughout the file. They can be made into a separate local API inside
> > telemetry, like telemetry_punit_send_cmd(). Same thoughout
> I will make this change in next version.
> >
> >>   static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig
> >> evtconfig, @@ -435,6 +434,7 @@ static int
> >> telemetry_setup_pssevtconfig(struct telemetry_evtconfig evtconfig,
> >>   	int ret, index, idx;
> >>   	u32 *pss_evtmap;
> >>   	u32 telem_ctrl;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	num_pss_evts = evtconfig.num_evts;
> >>   	pss_period = evtconfig.period;
> >> @@ -442,8 +442,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>
> >>   	/* PSS Config */
> >>   	/* Get telemetry EVENT CTL */
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> >> -				      0, 0, NULL, &telem_ctrl);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN, NULL,
> >> +			0, &telem_ctrl, 1, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Read Failed\n");
> >>   		return ret;
> >> @@ -451,8 +452,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>
> >>   	/* Disable Telemetry */
> >>   	TELEM_DISABLE(telem_ctrl);
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				      0, 0, &telem_ctrl, NULL);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Event Disable Write Failed\n");
> >>   		return ret;
> >> @@ -463,9 +465,10 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   		/* Clear All Events */
> >>   		TELEM_CLEAR_EVENTS(telem_ctrl);
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			return ret;
> >> @@ -489,9 +492,10 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   		/* Clear All Events */
> >>   		TELEM_CLEAR_EVENTS(telem_ctrl);
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			return ret;
> >> @@ -540,8 +544,9 @@ static int telemetry_setup_pssevtconfig(struct
> >> telemetry_evtconfig evtconfig,
> >>   	TELEM_ENABLE_PERIODIC(telem_ctrl);
> >>   	telem_ctrl |= pss_period;
> >>
> >> -	ret =
> >> intel_punit_ipc_command(IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				      0, 0, &telem_ctrl, NULL);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0, 0, 0);
> >>   	if (ret) {
> >>   		pr_err("PSS TELEM_CTRL Event Enable Write Failed\n");
> >>   		return ret;
> >> @@ -601,6 +606,7 @@ static int telemetry_setup(struct platform_device
> >> *pdev)  {
> >>   	struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig;
> >>   	u32 read_buf, events, event_regs;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>   	int ret;
> >>
> >>   	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
> >> IOSS_TELEM_INFO_READ, @@ -626,8 +632,9 @@ static int
> >> telemetry_setup(struct platform_device *pdev)
> >>   	telm_conf->ioss_config.max_period =
> TELEM_MAX_PERIOD(read_buf);
> >>
> >>   	/* PUNIT Mailbox Setup */
> >> -	ret = intel_punit_ipc_command(IPC_PUNIT_BIOS_READ_TELE_INFO,
> >> 0, 0,
> >> -				      NULL, &read_buf);
> >> +	punit_cmd_init(cmd, IPC_PUNIT_BIOS_READ_TELE_INFO, 0, 0);
> >> +	ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +			NULL, 0, &read_buf, 1, 0, 0);
> >>   	if (ret) {
> >>   		dev_err(&pdev->dev, "PSS TELEM_INFO Read Failed\n");
> >>   		return ret;
> >> @@ -695,6 +702,7 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)  {
> >>   	u32 telem_ctrl = 0;
> >>   	int ret = 0;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	mutex_lock(&(telm_conf->telem_lock));
> >>   	if (ioss_period) {
> >> @@ -752,9 +760,9 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>   		}
> >>
> >>   		/* Get telemetry EVENT CTL */
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL,
> >> -				0, 0, NULL, &telem_ctrl);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &telem_ctrl, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -762,9 +770,11 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>
> >>   		/* Disable Telemetry */
> >>   		TELEM_DISABLE(telem_ctrl);
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> +				0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Disable Write
> Failed\n");
> >>   			goto out;
> >> @@ -776,9 +786,11 @@ static int telemetry_plt_set_sampling_period(u8
> >> pss_period, u8 ioss_period)
> >>   		TELEM_ENABLE_PERIODIC(telem_ctrl);
> >>   		telem_ctrl |= pss_period;
> >>
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> -				0, 0, &telem_ctrl, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, 0,
> >> +				0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL,
> >> +				0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TELEM_CTRL Event Enable Write
> Failed\n");
> >>   			goto out;
> >> @@ -1013,6 +1025,7 @@ static int
> >> telemetry_plt_get_trace_verbosity(enum
> >> telemetry_unit telem_unit,  {
> >>   	u32 temp = 0;
> >>   	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	if (verbosity == NULL)
> >>   		return -EINVAL;
> >> @@ -1020,9 +1033,9 @@ static int
> >> telemetry_plt_get_trace_verbosity(enum
> >> telemetry_unit telem_unit,
> >>   	mutex_lock(&(telm_conf->telem_trace_lock));
> >>   	switch (telem_unit) {
> >>   	case TELEM_PSS:
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> >> -				0, 0, NULL, &temp);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &temp, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -1058,15 +1071,16 @@ static int
> >> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  {
> >>   	u32 temp = 0;
> >>   	int ret;
> >> +	u32 cmd[PUNIT_PARAM_LEN] = {0};
> >>
> >>   	verbosity &= TELEM_TRC_VERBOSITY_MASK;
> >>
> >>   	mutex_lock(&(telm_conf->telem_trace_lock));
> >>   	switch (telem_unit) {
> >>   	case TELEM_PSS:
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
> >> -				0, 0, NULL, &temp);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL, 0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				NULL, 0, &temp, 1, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Read Failed\n");
> >>   			goto out;
> >> @@ -1074,10 +1088,10 @@ static int
> >> telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
> >>
> >>   		TELEM_CLEAR_VERBOSITY_BITS(temp);
> >>   		TELEM_SET_VERBOSITY_BITS(temp, verbosity);
> >> -
> >> -		ret = intel_punit_ipc_command(
> >> -				IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
> >> -				0, 0, &temp, NULL);
> >> +		punit_cmd_init(cmd,
> >> IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL,
> >> +				0, 0);
> >> +		ret = ipc_dev_raw_cmd(punit_bios_ipc_dev, cmd,
> >> PUNIT_PARAM_LEN,
> >> +				(u8 *)&temp, sizeof(temp), NULL, 0, 0, 0);
> >>   		if (ret) {
> >>   			pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
> >>   			goto out;
> >> @@ -1139,6 +1153,10 @@ static int telemetry_pltdrv_probe(struct
> >> platform_device *pdev)
> >>   	if (!id)
> >>   		return -ENODEV;
> >>
> >> +	punit_bios_ipc_dev = intel_ipc_dev_get(PUNIT_BIOS_IPC_DEV);
> >> +	if (IS_ERR_OR_NULL(punit_bios_ipc_dev))
> >> +		return PTR_ERR(punit_bios_ipc_dev);
> >> +
> >>   	telm_conf = (struct telemetry_plt_config *)id->driver_data;
> >>
> >>   	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -
> >> 1218,6 +1236,7 @@ static int telemetry_pltdrv_probe(struct
> >> platform_device
> >> *pdev)  static int telemetry_pltdrv_remove(struct platform_device *pdev)
> {
> >>   	telemetry_clear_pltdata();
> >> +	intel_ipc_dev_put(punit_bios_ipc_dev);
> >>   	iounmap(telm_conf->pss_config.regmap);
> >>   	iounmap(telm_conf->ioss_config.regmap);
> >>
> >> --
> >> 2.7.4
> >
> 
> --
> Sathyanarayanan Kuppuswamy
> Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
  2017-10-10 22:09       ` sathyanarayanan kuppuswamy
  (?)
@ 2017-10-11  3:57         ` Chakravarty, Souvik K
  -1 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:57 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

On October 11, 2017 3:39 AM, Kuppuswamy Sathyanarayanan wrote:
> Hi,
> 
> 
> On 10/08/2017 09:53 PM, Chakravarty, Souvik K wrote:
> >> From: sathyanarayanan.kuppuswamy@linux.intel.com
> >> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> >> Sent: Sunday, October 8, 2017 3:50 AM
> >> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> >> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> >> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> >> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org;
> >> Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>
> >> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> >> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> >> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
> >>
> >> From: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >>
> >> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
> >> redundantly implements the same IPC features and has lot of code
> >> duplication between them. This driver addresses this issue by
> >> grouping the common IPC functionalities under the same driver.
> >>
> >> Signed-off-by: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> ---
> >>   drivers/platform/x86/Kconfig                    |   8 +
> >>   drivers/platform/x86/Makefile                   |   1 +
> >>   drivers/platform/x86/intel_ipc_dev.c            | 576
> >> ++++++++++++++++++++++++
> >>   include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
> >>   4 files changed, 791 insertions(+)
> >>   create mode 100644 drivers/platform/x86/intel_ipc_dev.c
> >>   create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
> >>
> >> Changes since v4:
> >>   * None
> >>
> >> Changes since v3:
> >>   * Fixed NULL pointer exception in intel_ipc_dev_get().
> >>   * Fixed error in check for duplicate intel_ipc_dev.
> >>   * Added custom interrupt handler support.
> >>   * Used char array for error string conversion.
> >>   * Added put dev support.
> >>   * Added devm_* variant of intel_ipc_dev_get().
> >>
> >> Changes since v2:
> >>   * Added ipc_dev_cmd API support.
> >>
> >> diff --git a/drivers/platform/x86/Kconfig
> >> b/drivers/platform/x86/Kconfig index da2d9ba..724ee696 100644
> >> --- a/drivers/platform/x86/Kconfig
> >> +++ b/drivers/platform/x86/Kconfig
> >> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
> >>   	  with the OS-image for the device. This option supplies the missing
> >>   	  information. Enable this for x86 tablets with Silead touchscreens.
> >>
> >> +config INTEL_IPC_DEV
> >> +	bool "Intel IPC Device Driver"
> >> +	depends on X86_64
> >> +	---help---
> >> +	  This driver implements core features of Intel IPC device. Devices
> >> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
> >> +	  driver to implement IPC protocol of their respective device.
> >> +
> >>   endif # X86_PLATFORM_DEVICES
> >>
> >>   config PMC_ATOM
> >> diff --git a/drivers/platform/x86/Makefile
> >> b/drivers/platform/x86/Makefile index 2b315d0..99a1af1 100644
> >> --- a/drivers/platform/x86/Makefile
> >> +++ b/drivers/platform/x86/Makefile
> >> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
> >> pmc_atom.o
> >>   obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
> >>   obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
> >>   obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
> >> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
> >> diff --git a/drivers/platform/x86/intel_ipc_dev.c
> >> b/drivers/platform/x86/intel_ipc_dev.c
> >> new file mode 100644
> >> index 0000000..f55ddec
> >> --- /dev/null
> >> +++ b/drivers/platform/x86/intel_ipc_dev.c
> >> @@ -0,0 +1,576 @@
> >> +/*
> >> + * intel_ipc_dev.c: Intel IPC device class driver
> >> + *
> >> + * (C) Copyright 2017 Intel Corporation
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License
> >> + * as published by the Free Software Foundation; version 2
> >> + * of the License.
> >> + *
> >> + */
> >> +
> >> +#include <linux/device.h>
> >> +#include <linux/module.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/err.h>
> >> +#include <linux/export.h>
> >> +#include <linux/idr.h>
> >> +#include <linux/init.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +#include <linux/regmap.h>
> >> +
> >> +/* mutex to sync different ipc devices in same channel */ static
> >> +struct mutex channel_lock[IPC_CHANNEL_MAX];
> >> +
> >> +static char *ipc_err_sources[] = {
> >> +	[IPC_DEV_ERR_NONE] =
> >> +		"No error",
> >> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
> >> +		"Command not-supported/Invalid",
> >> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
> >> +		"Command not-serviced/Invalid param",
> >> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
> >> +		"Unable-to-service/Cmd-timeout",
> >> +	[IPC_DEV_ERR_CMD_INVALID] =
> >> +		"Command-invalid/Cmd-locked",
> >> +	[IPC_DEV_ERR_CMD_FAILED] =
> >> +		"Command-failed/Invalid-VR-id",
> >> +	[IPC_DEV_ERR_EMSECURITY] =
> >> +		"Invalid Battery/VR-Error",
> >> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
> >> +		"Unsigned kernel",
> >> +};
> >> +
> >> +static void ipc_channel_lock_init(void) {
> >> +	int i;
> >> +
> >> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
> >> +		mutex_init(&channel_lock[i]);
> >> +}
> >> +
> >> +static struct class intel_ipc_class = {
> >> +	.name = "intel_ipc",
> >> +	.owner = THIS_MODULE,
> >> +};
> >> +
> >> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
> >> +	int chan_type;
> >> +
> >> +	if (!ipc_dev || !ipc_dev->cfg)
> >> +		return -ENODEV;
> >> +
> >> +	chan_type = ipc_dev->cfg->chan_type;
> >> +	if (chan_type > IPC_CHANNEL_MAX)
> >> +		return -EINVAL;
> >> +
> >> +	/* acquire channel lock */
> >> +	mutex_lock(&channel_lock[chan_type]);
> >> +
> >> +	/* acquire IPC device lock */
> >> +	mutex_lock(&ipc_dev->lock);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
> >> +	int chan_type;
> >> +
> >> +	if (!ipc_dev || !ipc_dev->cfg)
> >> +		return -ENODEV;
> >> +
> >> +	chan_type = ipc_dev->cfg->chan_type;
> >> +	if (chan_type > IPC_CHANNEL_MAX)
> >> +		return -EINVAL;
> >> +
> >> +	/* release IPC device lock */
> >> +	mutex_unlock(&ipc_dev->lock);
> >> +
> >> +	/* release channel lock */
> >> +	mutex_unlock(&channel_lock[chan_type]);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
> >> +	int error)
> >> +{
> >> +	if (error < IPC_DEV_ERR_MAX)
> >> +		return ipc_err_sources[error];
> >> +
> >> +	return "Unknown Command";
> >> +}
> >> +
> >> +/* Helper function to send given command to IPC device */ static
> >> +inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
> >> +	ipc_dev->cmd = cmd;
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
> >> +		reinit_completion(&ipc_dev->cmd_complete);
> >> +
> >> +	if (ipc_dev->ops->enable_msi)
> >> +		cmd = ipc_dev->ops->enable_msi(cmd);
> >> +
> >> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
> >> }
> >> +
> >> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
> >> +	int status;
> >> +
> >> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> >> +&status);
> >> +
> >> +	if (ipc_dev->ops->busy_check)
> >> +		return ipc_dev->ops->busy_check(status);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/* Check the status of IPC command and return err code if failed */
> >> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
> >> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
> >> +	int status;
> >> +	int ret = 0;
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> >> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
> >> +				IPC_DEV_CMD_TIMEOUT))
> >> +			ret = -ETIMEDOUT;
> >> +	} else {
> >> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
> >> +			udelay(1);
> >> +		if (!loop_count)
> >> +			ret = -ETIMEDOUT;
> >> +	}
> >> +
> >> +	if (ret < 0) {
> >> +		dev_err(&ipc_dev->dev,
> >> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
> >>> cmd);
> >> +		return ret;
> >> +	}
> >> +
> >> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> >> +&status);
> >> +
> >> +	if (ipc_dev->ops->to_err_code)
> >> +		ret = ipc_dev->ops->to_err_code(status);
> >> +
> >> +	if (ret) {
> >> +		dev_err(&ipc_dev->dev,
> >> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> >> +				ipc_dev_err_string(ipc_dev, ret),
> >> +				status, ipc_dev->cmd);
> >> +		return -EIO;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/**
> >> + * ipc_dev_simple_cmd() - Send simple IPC command
> >> + * @ipc_dev     : Reference to ipc device.
> >> + * @cmd_list    : IPC command list.
> >> + * @cmdlen      : Number of cmd/sub-cmds.
> >> + *
> >> + * Send a simple IPC command to ipc device.
> >> + * Use this when don't need to specify input/output data
> >> + * and source/dest pointers.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> >> +
> >> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen)
> >> +{
> >> +	int ret;
> >> +
> >> +	if (!cmd_list)
> >> +		return -EINVAL;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler */
> >> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
> >> +
> >> +/**
> >> + * ipc_dev_cmd() - Send IPC command with data.
> >> + * @ipc_dev     : Reference to ipc_dev.
> >> + * @cmd_list    : Array of commands/sub-commands.
> >> + * @cmdlen      : Number of commands.
> >> + * @in          : Input data of this IPC command.
> >> + * @inlen       : Input data length in dwords.
> >> + * @out         : Output data of this IPC command.
> >> + * @outlen      : Length of output data in dwords.
> >> + *
> >> + * Send an IPC command to device with input/output data.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> >> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> >> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
> >> +	int ret;
> >> +
> >> +	if (!cmd_list || !in)
> >> +		return -EINVAL;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler. */
> >> +	if (ipc_dev->ops->pre_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
> >> +				out, outlen);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	/* Write inlen dwords of data to wrbuf_reg. */
> >> +	if (inlen > 0)
> >> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +	/* Read outlen dwords of data from rbug_reg. */
> >> +	if (!ret && outlen > 0)
> >> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
> >> +
> >> +/**
> >> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
> >> + * @ipc_dev     : Reference to ipc_dev.
> >> + * @cmd_list    : Array of commands/sub-commands.
> >> + * @cmdlen      : Number of commands.
> >> + * @in          : Input data of this IPC command.
> >> + * @inlen       : Input data length in bytes.
> >> + * @out         : Output data of this IPC command.
> >> + * @outlen      : Length of output data in dwords.
> >> + * @dptr        : IPC destination data address.
> >> + * @sptr        : IPC source data address.
> >> + *
> >> + * Send an IPC command to device with input/output data and
> >> + * source/dest pointers.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> > Sorry for coming in so late but since we are refactoring the API
> > anyways, isn't it better to reduce the signature? Nine parameters is
> > an awful lot and prone to errors and difficult to debug. (We found it
> > out the hard way when we started using this a few years ago.)
> I agree. Initially I thought of adding a command structure just like you
> mentioned. But finally decided not to do it because,
> 
> 1. Not all drivers uses all parameters of this API. Most of them pass
> 0,0 for DPTR and SPTR pointers. So the last two arguments are almost not
> used.
> 2. Adding a new structure requires all users of this API to add buffer code /
> some additional call to initialize the command structure which in turn makes
> the code look bit complex.
> 
One can combine SPTR/DPTR into a separate struct could directly be passed as NULL for users who are not interested. That would take care of some complexity.
For the rest of the parameters, requiring the user to populate a struct for cmd_params could actually be useful. Since the user will be required to specifically initialize each parameter by name, it may prevent accidental jumbling of parameters (like mixing up in/out etc.) vis-a-vis passing all nine parameters in a row.
Plus it will be a lot better from the readability perspective.
So from my point of view, the little cmd_param assignment overhead is worth the benefits in readability + preventing accidental bug insertions + ease of debug.
FWIW I have not seen six+ parameters for any API in Linux.

> So I am not really sure whether it add any value. But if its the recommended
> approach then I will make that modification.
> > This can be consolidated into a few neat structs, e.g.,:
> > int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd,
> > 		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)
> >
> > Same for the ipc_dev_cmd() APIs above as well.
> >
> >> +
> >> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +u32
> >> cmdlen,
> >> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
> >> +	int ret;
> >> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
> >> +	u32 *inbuf;
> >> +
> >> +	if (!cmd_list || !in)
> >> +		return -EINVAL;
> >> +
> >> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
> >> +	if (!inbuf)
> >> +		return -ENOMEM;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler. */
> >> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
> >> inlen,
> >> +				out, outlen, dptr, sptr);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	/* If supported, write DPTR register.*/
> >> +	if (ipc_dev->cfg->support_dptr)
> >> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >>> dptr_reg,
> >> +				dptr);
> >> +
> >> +	/* If supported, write SPTR register. */
> >> +	if (ipc_dev->cfg->support_sptr)
> >> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >>> sptr_reg,
> >> +				sptr);
> >> +
> >> +	memcpy(inbuf, in, inlen);
> >> +
> >> +	/* Write inlen dwords of data to wrbuf_reg. */
> >> +	if (inlen > 0)
> >> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +	/* Read outlen dwords of data from rbug_reg. */
> >> +	if (!ret && outlen > 0)
> >> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +	kfree(inbuf);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
> >> +
> >> +/* sysfs option to send simple IPC commands from userspace */ static
> >> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
> >> +				     struct device_attribute *attr,
> >> +				     const char *buf, size_t count) {
> >> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
> >> +	u32 cmd;
> >> +	int ret;
> >> +
> >> +	ret = sscanf(buf, "%d", &cmd);
> >> +	if (ret != 1) {
> >> +		dev_err(dev, "Error args\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
> >> +	if (ret) {
> >> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
> >> +		return ret;
> >> +	}
> >> +	return (ssize_t)count;
> >> +}
> >> +
> >> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
> >> +
> >> +static struct attribute *ipc_dev_attrs[] = {
> >> +	&dev_attr_send_cmd.attr,
> >> +	NULL
> >> +};
> >> +
> >> +static const struct attribute_group ipc_dev_group = {
> >> +	.attrs = ipc_dev_attrs,
> >> +};
> >> +
> >> +static const struct attribute_group *ipc_dev_groups[] = {
> >> +	&ipc_dev_group,
> >> +	NULL,
> >> +};
> >> +
> >> +/* IPC device IRQ handler */
> >> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
> >> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
> >> +
> >> +	if (ipc_dev->ops->pre_irq_handler_fn)
> >> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
> >> +
> >> +	complete(&ipc_dev->cmd_complete);
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
> >> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
> >> +
> >> +	if (!ipc_dev)
> >> +		return;
> >> +
> >> +	device_del(&ipc_dev->dev);
> >> +
> >> +	kfree(ipc_dev);
> >> +}
> >> +
> >> +static int match_name(struct device *dev, const void *data) {
> >> +        if (!dev_name(dev))
> >> +                return 0;
> >> +
> >> +        return !strcmp(dev_name(dev), (char *)data); }
> >> +
> >> +/**
> >> + * intel_ipc_dev_get() - Get Intel IPC device from name.
> >> + * @dev_name    : Name of the IPC device.
> >> + *
> >> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> >> + */
> >> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
> >> +        struct device *dev;
> >> +
> >> +	if (!dev_name)
> >> +		return ERR_PTR(-EINVAL);
> >> +
> >> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
> >> match_name);
> >> +
> >> +	return dev ? dev_get_drvdata(dev) : NULL; }
> >> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
> >> +
> >> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
> >> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
> >> +
> >> +/**
> >> + * devm_intel_ipc_dev_get() - Resource managed version of
> >> intel_ipc_dev_get().
> >> + * @dev         : Device pointer.
> >> + * @dev_name    : Name of the IPC device.
> >> + *
> >> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> >> + */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> >> +					const char *dev_name)
> >> +{
> >> +	struct intel_ipc_dev **ptr, *ipc_dev;
> >> +
> >> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
> >> GFP_KERNEL);
> >> +	if (!ptr)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	ipc_dev = intel_ipc_dev_get(dev_name);
> >> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
> >> +		*ptr = ipc_dev;
> >> +		devres_add(dev, ptr);
> >> +	} else {
> >> +		devres_free(ptr);
> >> +	}
> >> +
> >> +	return ipc_dev;
> >> +}
> >> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
> >> +
> >> +/**
> >> + * devm_intel_ipc_dev_create() - Create IPC device
> >> + * @dev		: IPC parent device.
> >> + * @devname	: Name of the IPC device.
> >> + * @cfg		: IPC device configuration.
> >> + * @ops		: IPC device ops.
> >> + *
> >> + * Resource managed API to create IPC device with
> >> + * given configuration.
> >> + *
> >> + * Return	: IPC device pointer or ERR_PTR(error code).
> >> + */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> >> +		const char *devname,
> >> +		struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops)
> >> +{
> >> +	struct intel_ipc_dev **ptr, *ipc_dev;
> >> +	int ret;
> >> +
> >> +	if (!dev && !devname && !cfg)
> >> +		return ERR_PTR(-EINVAL);
> >> +
> >> +	if (intel_ipc_dev_get(devname)) {
> >> +		dev_err(dev, "IPC device %s already exist\n", devname);
> >> +		return ERR_PTR(-EINVAL);
> >> +	}
> >> +
> >> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
> >> +			GFP_KERNEL);
> >> +	if (!ptr)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
> >> +	if (!ipc_dev) {
> >> +		ret = -ENOMEM;
> >> +		goto err_dev_create;
> >> +	}
> >> +
> >> +	ipc_dev->dev.class = &intel_ipc_class;
> >> +	ipc_dev->dev.parent = dev;
> >> +	ipc_dev->dev.groups = ipc_dev_groups;
> >> +	ipc_dev->cfg = cfg;
> >> +	ipc_dev->ops = ops;
> >> +
> >> +	mutex_init(&ipc_dev->lock);
> >> +	init_completion(&ipc_dev->cmd_complete);
> >> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
> >> +	dev_set_name(&ipc_dev->dev, devname);
> >> +	device_initialize(&ipc_dev->dev);
> >> +
> >> +	ret = device_add(&ipc_dev->dev);
> >> +	if (ret < 0) {
> >> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
> >> +				__func__);
> >> +		ret = -ENODEV;
> >> +		goto err_dev_add;
> >> +	}
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> >> +		if (devm_request_irq(&ipc_dev->dev,
> >> +				ipc_dev->cfg->irq,
> >> +				ipc_dev_irq_handler,
> >> +				ipc_dev->cfg->irqflags,
> >> +				dev_name(&ipc_dev->dev),
> >> +				ipc_dev)) {
> >> +			dev_err(&ipc_dev->dev,
> >> +					"Failed to request irq\n");
> >> +			goto err_irq_request;
> >> +		}
> >> +	}
> >> +
> >> +	*ptr = ipc_dev;
> >> +
> >> +	devres_add(dev, ptr);
> >> +
> >> +	return ipc_dev;
> >> +
> >> +err_irq_request:
> >> +	device_del(&ipc_dev->dev);
> >> +err_dev_add:
> >> +	kfree(ipc_dev);
> >> +err_dev_create:
> >> +	devres_free(ptr);
> >> +	return ERR_PTR(ret);
> >> +}
> >> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
> >> +
> >> +static int __init intel_ipc_init(void) {
> >> +	ipc_channel_lock_init();
> >> +	return class_register(&intel_ipc_class); }
> >> +
> >> +static void __exit intel_ipc_exit(void) {
> >> +	class_unregister(&intel_ipc_class);
> >> +}
> >> +subsys_initcall(intel_ipc_init);
> >> +module_exit(intel_ipc_exit);
> >> +
> >> +MODULE_LICENSE("GPL v2");
> >> +MODULE_AUTHOR("Kuppuswamy
> >> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
> >> +MODULE_DESCRIPTION("Intel IPC device class driver");
> >> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
> >> b/include/linux/platform_data/x86/intel_ipc_dev.h
> >> new file mode 100644
> >> index 0000000..eaeedaf
> >> --- /dev/null
> >> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
> >> @@ -0,0 +1,206 @@
> >> +/*
> >> + * Intel IPC class device header file.
> >> + *
> >> + * (C) Copyright 2017 Intel Corporation
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License
> >> + * as published by the Free Software Foundation; version 2
> >> + * of the License.
> >> + *
> >> + */
> >> +
> >> +#ifndef INTEL_IPC_DEV_H
> >> +#define INTEL_IPC_DEV_H
> >> +
> >> +#include <linux/module.h>
> >> +#include <linux/device.h>
> >> +
> >> +/* IPC channel type */
> >> +#define IPC_CHANNEL_IA_PMC                      0
> >> +#define IPC_CHANNEL_IA_PUNIT                    1
> >> +#define IPC_CHANNEL_PMC_PUNIT                   2
> >> +#define IPC_CHANNEL_IA_SCU                      3
> >> +#define IPC_CHANNEL_MAX                         4
> >> +
> >> +/* IPC return code */
> >> +#define IPC_DEV_ERR_NONE			0
> >> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
> >> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
> >> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
> >> +#define IPC_DEV_ERR_CMD_INVALID			4
> >> +#define IPC_DEV_ERR_CMD_FAILED			5
> >> +#define IPC_DEV_ERR_EMSECURITY			6
> >> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
> >> +#define IPC_DEV_ERR_MAX				8
> >> +
> >> +/* IPC mode */
> >> +#define IPC_DEV_MODE_IRQ			0
> >> +#define IPC_DEV_MODE_POLLING			1
> >> +
> >> +/* IPC dev constants */
> >> +#define IPC_DEV_CMD_LOOP_CNT			3000000
> >> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
> >> +#define IPC_DEV_DATA_BUFFER_SIZE		16
> >> +
> >> +struct intel_ipc_dev;
> >> +struct intel_ipc_raw_cmd;
> >> +
> >> +/**
> >> + * struct intel_ipc_dev_cfg - IPC device config structure.
> >> + *
> >> + * IPC device drivers uses the following config options to
> >> + * register new IPC device.
> >> + *
> >> + * @cmd_regs            : IPC device command base regmap.
> >> + * @data_regs           : IPC device data base regmap.
> >> + * @wrbuf_reg           : IPC device data write register address.
> >> + * @rbuf_reg            : IPC device data read register address.
> >> + * @sptr_reg            : IPC device source data pointer register address.
> >> + * @dptr_reg            : IPC device destination data pointer register
> >> + *                        address.
> >> + * @status_reg          : IPC command status register address.
> >> + * @cmd_reg             : IPC command register address.
> >> + * @mode                : IRQ/POLLING mode.
> >> + * @irq                 : IPC device IRQ number.
> >> + * @irqflags            : IPC device IRQ flags.
> >> + * @chan_type           : IPC device channel type(PMC/PUNIT).
> >> + * @msi                 : Enable/Disable MSI for IPC commands.
> >> + * @support_dptr        : Support DPTR update.
> >> + * @support_sptr        : Support SPTR update.
> >> + *
> >> + */
> >> +struct intel_ipc_dev_cfg {
> >> +	struct regmap *cmd_regs;
> >> +	struct regmap *data_regs;
> >> +	unsigned int wrbuf_reg;
> >> +	unsigned int rbuf_reg;
> >> +	unsigned int sptr_reg;
> >> +	unsigned int dptr_reg;
> >> +	unsigned int status_reg;
> >> +	unsigned int cmd_reg;
> >> +	int mode;
> >> +	int irq;
> >> +	int irqflags;
> >> +	int chan_type;
> >> +	bool use_msi;
> >> +	bool support_dptr;
> >> +	bool support_sptr;
> >> +};
> >> +
> >> +/**
> >> + * struct intel_ipc_dev_ops - IPC device ops structure.
> >> + *
> >> + * Call backs for IPC device specific operations.
> >> + *
> >> + * @to_err_code         : Status to error code conversion function.
> >> + * @busy_check          : Check for IPC busy status.
> >> + * @enable_msi          : Enable MSI for IPC commands.
> >> + * @pre_simple_cmd_fn   : Custom pre-processing function for
> >> + *                        ipc_dev_simple_cmd()
> >> + * @pre_cmd_fn          : Custom pre-processing function for
> >> + *                        ipc_dev_cmd()
> >> + * @pre_raw_cmd_fn      : Custom pre-processing function for
> >> + *                        ipc_dev_raw_cmd()
> >> + *
> >> + */
> >> +struct intel_ipc_dev_ops {
> >> +	int (*to_err_code)(int status);
> >> +	int (*busy_check)(int status);
> >> +	u32 (*enable_msi)(u32 cmd);
> >> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
> >> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> >> +			u32 *out, u32 outlen);
> >> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> >> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
> >> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq);
> >> +};
> >> +
> >> +/**
> >> + * struct intel_ipc_dev - Intel IPC device structure.
> >> + *
> >> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
> >> + *
> >> + * @dev                 : IPC device object.
> >> + * @cmd                 : Current IPC device command.
> >> + * @cmd_complete        : Command completion object.
> >> + * @lock                : Lock to protect IPC device structure.
> >> + * @ops                 : IPC device ops pointer.
> >> + * @cfg                 : IPC device cfg pointer.
> >> + *
> >> + */
> >> +struct intel_ipc_dev {
> >> +	struct device dev;
> >> +	int cmd;
> >> +	struct completion cmd_complete;
> >> +	struct mutex lock;
> >> +	struct intel_ipc_dev_ops *ops;
> >> +	struct intel_ipc_dev_cfg *cfg;
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
> >> +
> >> +/* API to create new IPC device */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> >> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops);
> >> +
> >> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen);
> >> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> >> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
> >> ipc_dev_raw_cmd(struct
> >> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> >> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
> >> struct
> >> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
> >> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> >> +					const char *dev_name);
> >> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> >> +	put_device(&ipc_dev->dev);
> >> +}
> >> +#else
> >> +
> >> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
> >> +		struct device *dev,
> >> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops)
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
> >> +		u32 *cmd_list, u32 cmdlen)
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
> >> *cmd_list,
> >> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
> >> +		u32 dptr, u32 sptr);
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
> >> +*dev_name) {
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct
> >> +device
> >> *dev,
> >> +					const char *dev_name);
> >> +{
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> >> +	return NULL;
> >> +}
> >> +#endif /* CONFIG_INTEL_IPC_DEV */
> >> +#endif /* INTEL_IPC_DEV_H */
> >> --
> >> 2.7.4
> >
> 
> --
> Sathyanarayanan Kuppuswamy
> Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-11  3:57         ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:57 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

T24gT2N0b2JlciAxMSwgMjAxNyAzOjM5IEFNLCBLdXBwdXN3YW15IFNhdGh5YW5hcmF5YW5hbiB3
cm90ZToNCj4gSGksDQo+IA0KPiANCj4gT24gMTAvMDgvMjAxNyAwOTo1MyBQTSwgQ2hha3JhdmFy
dHksIFNvdXZpayBLIHdyb3RlOg0KPiA+PiBGcm9tOiBzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2Ft
eUBsaW51eC5pbnRlbC5jb20NCj4gPj4gW21haWx0bzpzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2Ft
eUBsaW51eC5pbnRlbC5jb21dDQo+ID4+IFNlbnQ6IFN1bmRheSwgT2N0b2JlciA4LCAyMDE3IDM6
NTAgQU0NCj4gPj4gVG86IGEuenVtbW9AdG93ZXJ0ZWNoLml0OyB4ODZAa2VybmVsLm9yZzsgd2lt
QGlndWFuYS5iZTsNCj4gPj4gbWluZ29AcmVkaGF0LmNvbTsgYWxleGFuZHJlLmJlbGxvbmlAZnJl
ZS1lbGVjdHJvbnMuY29tOyBaaGEsIFFpcGVuZw0KPiA+PiA8cWlwZW5nLnpoYUBpbnRlbC5jb20+
OyBocGFAenl0b3IuY29tOyBkdmhhcnRAaW5mcmFkZWFkLm9yZzsNCj4gPj4gdGdseEBsaW51dHJv
bml4LmRlOyBsZWUuam9uZXNAbGluYXJvLm9yZzsgYW5keUBpbmZyYWRlYWQub3JnOw0KPiA+PiBD
aGFrcmF2YXJ0eSwgU291dmlrIEsgPHNvdXZpay5rLmNoYWtyYXZhcnR5QGludGVsLmNvbT4NCj4g
Pj4gQ2M6IGxpbnV4LXJ0Y0B2Z2VyLmtlcm5lbC5vcmc7IGxpbnV4LXdhdGNoZG9nQHZnZXIua2Vy
bmVsLm9yZzsgbGludXgtDQo+ID4+IGtlcm5lbEB2Z2VyLmtlcm5lbC5vcmc7IHBsYXRmb3JtLWRy
aXZlci14ODZAdmdlci5rZXJuZWwub3JnOw0KPiA+PiBzYXRoeWFvc2lkQGdtYWlsLmNvbTsgS3Vw
cHVzd2FteSBTYXRoeWFuYXJheWFuYW4NCj4gPj4gPHNhdGh5YW5hcmF5YW5hbi5rdXBwdXN3YW15
QGxpbnV4LmludGVsLmNvbT4NCj4gPj4gU3ViamVjdDogW1JGQyB2NSA0LzhdIHBsYXRmb3JtOiB4
ODY6IEFkZCBnZW5lcmljIEludGVsIElQQyBkcml2ZXINCj4gPj4NCj4gPj4gRnJvbTogS3VwcHVz
d2FteSBTYXRoeWFuYXJheWFuYW4NCj4gPj4gPHNhdGh5YW5hcmF5YW5hbi5rdXBwdXN3YW15QGxp
bnV4LmludGVsLmNvbT4NCj4gPj4NCj4gPj4gQ3VycmVudGx5IGludGVsX3NjdV9pcGMuYywgaW50
ZWxfcG1jX2lwYy5jIGFuZCBpbnRlbF9wdW5pdF9pcGMuYw0KPiA+PiByZWR1bmRhbnRseSBpbXBs
ZW1lbnRzIHRoZSBzYW1lIElQQyBmZWF0dXJlcyBhbmQgaGFzIGxvdCBvZiBjb2RlDQo+ID4+IGR1
cGxpY2F0aW9uIGJldHdlZW4gdGhlbS4gVGhpcyBkcml2ZXIgYWRkcmVzc2VzIHRoaXMgaXNzdWUg
YnkNCj4gPj4gZ3JvdXBpbmcgdGhlIGNvbW1vbiBJUEMgZnVuY3Rpb25hbGl0aWVzIHVuZGVyIHRo
ZSBzYW1lIGRyaXZlci4NCj4gPj4NCj4gPj4gU2lnbmVkLW9mZi1ieTogS3VwcHVzd2FteSBTYXRo
eWFuYXJheWFuYW4NCj4gPj4gPHNhdGh5YW5hcmF5YW5hbi5rdXBwdXN3YW15QGxpbnV4LmludGVs
LmNvbT4NCj4gPj4gLS0tDQo+ID4+ICAgZHJpdmVycy9wbGF0Zm9ybS94ODYvS2NvbmZpZyAgICAg
ICAgICAgICAgICAgICAgfCAgIDggKw0KPiA+PiAgIGRyaXZlcnMvcGxhdGZvcm0veDg2L01ha2Vm
aWxlICAgICAgICAgICAgICAgICAgIHwgICAxICsNCj4gPj4gICBkcml2ZXJzL3BsYXRmb3JtL3g4
Ni9pbnRlbF9pcGNfZGV2LmMgICAgICAgICAgICB8IDU3Ng0KPiA+PiArKysrKysrKysrKysrKysr
KysrKysrKysNCj4gPj4gICBpbmNsdWRlL2xpbnV4L3BsYXRmb3JtX2RhdGEveDg2L2ludGVsX2lw
Y19kZXYuaCB8IDIwNiArKysrKysrKysNCj4gPj4gICA0IGZpbGVzIGNoYW5nZWQsIDc5MSBpbnNl
cnRpb25zKCspDQo+ID4+ICAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvcGxhdGZvcm0veDg2
L2ludGVsX2lwY19kZXYuYw0KPiA+PiAgIGNyZWF0ZSBtb2RlIDEwMDY0NCBpbmNsdWRlL2xpbnV4
L3BsYXRmb3JtX2RhdGEveDg2L2ludGVsX2lwY19kZXYuaA0KPiA+Pg0KPiA+PiBDaGFuZ2VzIHNp
bmNlIHY0Og0KPiA+PiAgICogTm9uZQ0KPiA+Pg0KPiA+PiBDaGFuZ2VzIHNpbmNlIHYzOg0KPiA+
PiAgICogRml4ZWQgTlVMTCBwb2ludGVyIGV4Y2VwdGlvbiBpbiBpbnRlbF9pcGNfZGV2X2dldCgp
Lg0KPiA+PiAgICogRml4ZWQgZXJyb3IgaW4gY2hlY2sgZm9yIGR1cGxpY2F0ZSBpbnRlbF9pcGNf
ZGV2Lg0KPiA+PiAgICogQWRkZWQgY3VzdG9tIGludGVycnVwdCBoYW5kbGVyIHN1cHBvcnQuDQo+
ID4+ICAgKiBVc2VkIGNoYXIgYXJyYXkgZm9yIGVycm9yIHN0cmluZyBjb252ZXJzaW9uLg0KPiA+
PiAgICogQWRkZWQgcHV0IGRldiBzdXBwb3J0Lg0KPiA+PiAgICogQWRkZWQgZGV2bV8qIHZhcmlh
bnQgb2YgaW50ZWxfaXBjX2Rldl9nZXQoKS4NCj4gPj4NCj4gPj4gQ2hhbmdlcyBzaW5jZSB2MjoN
Cj4gPj4gICAqIEFkZGVkIGlwY19kZXZfY21kIEFQSSBzdXBwb3J0Lg0KPiA+Pg0KPiA+PiBkaWZm
IC0tZ2l0IGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvS2NvbmZpZw0KPiA+PiBiL2RyaXZlcnMvcGxh
dGZvcm0veDg2L0tjb25maWcgaW5kZXggZGEyZDliYS4uNzI0ZWU2OTYgMTAwNjQ0DQo+ID4+IC0t
LSBhL2RyaXZlcnMvcGxhdGZvcm0veDg2L0tjb25maWcNCj4gPj4gKysrIGIvZHJpdmVycy9wbGF0
Zm9ybS94ODYvS2NvbmZpZw0KPiA+PiBAQCAtMTE1Myw2ICsxMTUzLDE0IEBAIGNvbmZpZyBTSUxF
QURfRE1JDQo+ID4+ICAgCSAgd2l0aCB0aGUgT1MtaW1hZ2UgZm9yIHRoZSBkZXZpY2UuIFRoaXMg
b3B0aW9uIHN1cHBsaWVzIHRoZSBtaXNzaW5nDQo+ID4+ICAgCSAgaW5mb3JtYXRpb24uIEVuYWJs
ZSB0aGlzIGZvciB4ODYgdGFibGV0cyB3aXRoIFNpbGVhZCB0b3VjaHNjcmVlbnMuDQo+ID4+DQo+
ID4+ICtjb25maWcgSU5URUxfSVBDX0RFVg0KPiA+PiArCWJvb2wgIkludGVsIElQQyBEZXZpY2Ug
RHJpdmVyIg0KPiA+PiArCWRlcGVuZHMgb24gWDg2XzY0DQo+ID4+ICsJLS0taGVscC0tLQ0KPiA+
PiArCSAgVGhpcyBkcml2ZXIgaW1wbGVtZW50cyBjb3JlIGZlYXR1cmVzIG9mIEludGVsIElQQyBk
ZXZpY2UuIERldmljZXMNCj4gPj4gKwkgIGxpa2UgUE1DLCBTQ1UsIFBVTklULCBldGMgY2FuIHVz
ZSBpbnRlcmZhY2VzIHByb3ZpZGVkIGJ5IHRoaXMNCj4gPj4gKwkgIGRyaXZlciB0byBpbXBsZW1l
bnQgSVBDIHByb3RvY29sIG9mIHRoZWlyIHJlc3BlY3RpdmUgZGV2aWNlLg0KPiA+PiArDQo+ID4+
ICAgZW5kaWYgIyBYODZfUExBVEZPUk1fREVWSUNFUw0KPiA+Pg0KPiA+PiAgIGNvbmZpZyBQTUNf
QVRPTQ0KPiA+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvTWFrZWZpbGUNCj4g
Pj4gYi9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9NYWtlZmlsZSBpbmRleCAyYjMxNWQwLi45OWExYWYx
IDEwMDY0NA0KPiA+PiAtLS0gYS9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9NYWtlZmlsZQ0KPiA+PiAr
KysgYi9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9NYWtlZmlsZQ0KPiA+PiBAQCAtODQsMyArODQsNCBA
QCBvYmotJChDT05GSUdfUE1DX0FUT00pCQkrPQ0KPiA+PiBwbWNfYXRvbS5vDQo+ID4+ICAgb2Jq
LSQoQ09ORklHX01MWF9QTEFURk9STSkJKz0gbWx4LXBsYXRmb3JtLm8NCj4gPj4gICBvYmotJChD
T05GSUdfTUxYX0NQTERfUExBVEZPUk0pCSs9IG1seGNwbGQtaG90cGx1Zy5vDQo+ID4+ICAgb2Jq
LSQoQ09ORklHX0lOVEVMX1RVUkJPX01BWF8zKSArPSBpbnRlbF90dXJib19tYXhfMy5vDQo+ID4+
ICtvYmotJChDT05GSUdfSU5URUxfSVBDX0RFVikJKz0gaW50ZWxfaXBjX2Rldi5vDQo+ID4+IGRp
ZmYgLS1naXQgYS9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9pcGNfZGV2LmMNCj4gPj4gYi9k
cml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9pcGNfZGV2LmMNCj4gPj4gbmV3IGZpbGUgbW9kZSAx
MDA2NDQNCj4gPj4gaW5kZXggMDAwMDAwMC4uZjU1ZGRlYw0KPiA+PiAtLS0gL2Rldi9udWxsDQo+
ID4+ICsrKyBiL2RyaXZlcnMvcGxhdGZvcm0veDg2L2ludGVsX2lwY19kZXYuYw0KPiA+PiBAQCAt
MCwwICsxLDU3NiBAQA0KPiA+PiArLyoNCj4gPj4gKyAqIGludGVsX2lwY19kZXYuYzogSW50ZWwg
SVBDIGRldmljZSBjbGFzcyBkcml2ZXINCj4gPj4gKyAqDQo+ID4+ICsgKiAoQykgQ29weXJpZ2h0
IDIwMTcgSW50ZWwgQ29ycG9yYXRpb24NCj4gPj4gKyAqDQo+ID4+ICsgKiBUaGlzIHByb2dyYW0g
aXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yDQo+ID4+ICsg
KiBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGlj
ZW5zZQ0KPiA+PiArICogYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRp
b247IHZlcnNpb24gMg0KPiA+PiArICogb2YgdGhlIExpY2Vuc2UuDQo+ID4+ICsgKg0KPiA+PiAr
ICovDQo+ID4+ICsNCj4gPj4gKyNpbmNsdWRlIDxsaW51eC9kZXZpY2UuaD4NCj4gPj4gKyNpbmNs
dWRlIDxsaW51eC9tb2R1bGUuaD4NCj4gPj4gKyNpbmNsdWRlIDxsaW51eC9kZWxheS5oPg0KPiA+
PiArI2luY2x1ZGUgPGxpbnV4L2Vyci5oPg0KPiA+PiArI2luY2x1ZGUgPGxpbnV4L2V4cG9ydC5o
Pg0KPiA+PiArI2luY2x1ZGUgPGxpbnV4L2lkci5oPg0KPiA+PiArI2luY2x1ZGUgPGxpbnV4L2lu
aXQuaD4NCj4gPj4gKyNpbmNsdWRlIDxsaW51eC9zbGFiLmg+DQo+ID4+ICsjaW5jbHVkZSA8bGlu
dXgvaW50ZXJydXB0Lmg+DQo+ID4+ICsjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGF0YS94ODYv
aW50ZWxfaXBjX2Rldi5oPg0KPiA+PiArI2luY2x1ZGUgPGxpbnV4L3JlZ21hcC5oPg0KPiA+PiAr
DQo+ID4+ICsvKiBtdXRleCB0byBzeW5jIGRpZmZlcmVudCBpcGMgZGV2aWNlcyBpbiBzYW1lIGNo
YW5uZWwgKi8gc3RhdGljDQo+ID4+ICtzdHJ1Y3QgbXV0ZXggY2hhbm5lbF9sb2NrW0lQQ19DSEFO
TkVMX01BWF07DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBjaGFyICppcGNfZXJyX3NvdXJjZXNbXSA9
IHsNCj4gPj4gKwlbSVBDX0RFVl9FUlJfTk9ORV0gPQ0KPiA+PiArCQkiTm8gZXJyb3IiLA0KPiA+
PiArCVtJUENfREVWX0VSUl9DTURfTk9UX1NVUFBPUlRFRF0gPQ0KPiA+PiArCQkiQ29tbWFuZCBu
b3Qtc3VwcG9ydGVkL0ludmFsaWQiLA0KPiA+PiArCVtJUENfREVWX0VSUl9DTURfTk9UX1NFUlZJ
Q0VEXSA9DQo+ID4+ICsJCSJDb21tYW5kIG5vdC1zZXJ2aWNlZC9JbnZhbGlkIHBhcmFtIiwNCj4g
Pj4gKwlbSVBDX0RFVl9FUlJfVU5BQkxFX1RPX1NFUlZJQ0VdID0NCj4gPj4gKwkJIlVuYWJsZS10
by1zZXJ2aWNlL0NtZC10aW1lb3V0IiwNCj4gPj4gKwlbSVBDX0RFVl9FUlJfQ01EX0lOVkFMSURd
ID0NCj4gPj4gKwkJIkNvbW1hbmQtaW52YWxpZC9DbWQtbG9ja2VkIiwNCj4gPj4gKwlbSVBDX0RF
Vl9FUlJfQ01EX0ZBSUxFRF0gPQ0KPiA+PiArCQkiQ29tbWFuZC1mYWlsZWQvSW52YWxpZC1WUi1p
ZCIsDQo+ID4+ICsJW0lQQ19ERVZfRVJSX0VNU0VDVVJJVFldID0NCj4gPj4gKwkJIkludmFsaWQg
QmF0dGVyeS9WUi1FcnJvciIsDQo+ID4+ICsJW0lQQ19ERVZfRVJSX1VOU0lHTkVES0VSTkVMXSA9
DQo+ID4+ICsJCSJVbnNpZ25lZCBrZXJuZWwiLA0KPiA+PiArfTsNCj4gPj4gKw0KPiA+PiArc3Rh
dGljIHZvaWQgaXBjX2NoYW5uZWxfbG9ja19pbml0KHZvaWQpIHsNCj4gPj4gKwlpbnQgaTsNCj4g
Pj4gKw0KPiA+PiArCWZvciAoaSA9IDA7IGkgPCBJUENfQ0hBTk5FTF9NQVg7IGkrKykNCj4gPj4g
KwkJbXV0ZXhfaW5pdCgmY2hhbm5lbF9sb2NrW2ldKTsNCj4gPj4gK30NCj4gPj4gKw0KPiA+PiAr
c3RhdGljIHN0cnVjdCBjbGFzcyBpbnRlbF9pcGNfY2xhc3MgPSB7DQo+ID4+ICsJLm5hbWUgPSAi
aW50ZWxfaXBjIiwNCj4gPj4gKwkub3duZXIgPSBUSElTX01PRFVMRSwNCj4gPj4gK307DQo+ID4+
ICsNCj4gPj4gK3N0YXRpYyBpbnQgaXBjX2Rldl9sb2NrKHN0cnVjdCBpbnRlbF9pcGNfZGV2ICpp
cGNfZGV2KSB7DQo+ID4+ICsJaW50IGNoYW5fdHlwZTsNCj4gPj4gKw0KPiA+PiArCWlmICghaXBj
X2RldiB8fCAhaXBjX2Rldi0+Y2ZnKQ0KPiA+PiArCQlyZXR1cm4gLUVOT0RFVjsNCj4gPj4gKw0K
PiA+PiArCWNoYW5fdHlwZSA9IGlwY19kZXYtPmNmZy0+Y2hhbl90eXBlOw0KPiA+PiArCWlmIChj
aGFuX3R5cGUgPiBJUENfQ0hBTk5FTF9NQVgpDQo+ID4+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+
PiArDQo+ID4+ICsJLyogYWNxdWlyZSBjaGFubmVsIGxvY2sgKi8NCj4gPj4gKwltdXRleF9sb2Nr
KCZjaGFubmVsX2xvY2tbY2hhbl90eXBlXSk7DQo+ID4+ICsNCj4gPj4gKwkvKiBhY3F1aXJlIElQ
QyBkZXZpY2UgbG9jayAqLw0KPiA+PiArCW11dGV4X2xvY2soJmlwY19kZXYtPmxvY2spOw0KPiA+
PiArDQo+ID4+ICsJcmV0dXJuIDA7DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBpbnQg
aXBjX2Rldl91bmxvY2soc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYpIHsNCj4gPj4gKwlp
bnQgY2hhbl90eXBlOw0KPiA+PiArDQo+ID4+ICsJaWYgKCFpcGNfZGV2IHx8ICFpcGNfZGV2LT5j
ZmcpDQo+ID4+ICsJCXJldHVybiAtRU5PREVWOw0KPiA+PiArDQo+ID4+ICsJY2hhbl90eXBlID0g
aXBjX2Rldi0+Y2ZnLT5jaGFuX3R5cGU7DQo+ID4+ICsJaWYgKGNoYW5fdHlwZSA+IElQQ19DSEFO
TkVMX01BWCkNCj4gPj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+ICsNCj4gPj4gKwkvKiByZWxl
YXNlIElQQyBkZXZpY2UgbG9jayAqLw0KPiA+PiArCW11dGV4X3VubG9jaygmaXBjX2Rldi0+bG9j
ayk7DQo+ID4+ICsNCj4gPj4gKwkvKiByZWxlYXNlIGNoYW5uZWwgbG9jayAqLw0KPiA+PiArCW11
dGV4X3VubG9jaygmY2hhbm5lbF9sb2NrW2NoYW5fdHlwZV0pOw0KPiA+PiArDQo+ID4+ICsJcmV0
dXJuIDA7DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBjb25zdCBjaGFyICppcGNfZGV2
X2Vycl9zdHJpbmcoc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYsDQo+ID4+ICsJaW50IGVy
cm9yKQ0KPiA+PiArew0KPiA+PiArCWlmIChlcnJvciA8IElQQ19ERVZfRVJSX01BWCkNCj4gPj4g
KwkJcmV0dXJuIGlwY19lcnJfc291cmNlc1tlcnJvcl07DQo+ID4+ICsNCj4gPj4gKwlyZXR1cm4g
IlVua25vd24gQ29tbWFuZCI7DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gKy8qIEhlbHBlciBmdW5j
dGlvbiB0byBzZW5kIGdpdmVuIGNvbW1hbmQgdG8gSVBDIGRldmljZSAqLyBzdGF0aWMNCj4gPj4g
K2lubGluZSB2b2lkIGlwY19kZXZfc2VuZF9jbWQoc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19k
ZXYsIHUzMiBjbWQpIHsNCj4gPj4gKwlpcGNfZGV2LT5jbWQgPSBjbWQ7DQo+ID4+ICsNCj4gPj4g
KwlpZiAoaXBjX2Rldi0+Y2ZnLT5tb2RlID09IElQQ19ERVZfTU9ERV9JUlEpDQo+ID4+ICsJCXJl
aW5pdF9jb21wbGV0aW9uKCZpcGNfZGV2LT5jbWRfY29tcGxldGUpOw0KPiA+PiArDQo+ID4+ICsJ
aWYgKGlwY19kZXYtPm9wcy0+ZW5hYmxlX21zaSkNCj4gPj4gKwkJY21kID0gaXBjX2Rldi0+b3Bz
LT5lbmFibGVfbXNpKGNtZCk7DQo+ID4+ICsNCj4gPj4gKwlyZWdtYXBfd3JpdGUoaXBjX2Rldi0+
Y2ZnLT5jbWRfcmVncywgaXBjX2Rldi0+Y2ZnLT5jbWRfcmVnLCBjbWQpOw0KPiA+PiB9DQo+ID4+
ICsNCj4gPj4gK3N0YXRpYyBpbmxpbmUgaW50IGlwY19kZXZfc3RhdHVzX2J1c3koc3RydWN0IGlu
dGVsX2lwY19kZXYgKmlwY19kZXYpIHsNCj4gPj4gKwlpbnQgc3RhdHVzOw0KPiA+PiArDQo+ID4+
ICsJcmVnbWFwX3JlYWQoaXBjX2Rldi0+Y2ZnLT5jbWRfcmVncywgaXBjX2Rldi0+Y2ZnLT5zdGF0
dXNfcmVnLA0KPiA+PiArJnN0YXR1cyk7DQo+ID4+ICsNCj4gPj4gKwlpZiAoaXBjX2Rldi0+b3Bz
LT5idXN5X2NoZWNrKQ0KPiA+PiArCQlyZXR1cm4gaXBjX2Rldi0+b3BzLT5idXN5X2NoZWNrKHN0
YXR1cyk7DQo+ID4+ICsNCj4gPj4gKwlyZXR1cm4gMDsNCj4gPj4gK30NCj4gPj4gKw0KPiA+PiAr
LyogQ2hlY2sgdGhlIHN0YXR1cyBvZiBJUEMgY29tbWFuZCBhbmQgcmV0dXJuIGVyciBjb2RlIGlm
IGZhaWxlZCAqLw0KPiA+PiArc3RhdGljIGludCBpcGNfZGV2X2NoZWNrX3N0YXR1cyhzdHJ1Y3Qg
aW50ZWxfaXBjX2RldiAqaXBjX2Rldikgew0KPiA+PiArCWludCBsb29wX2NvdW50ID0gSVBDX0RF
Vl9DTURfTE9PUF9DTlQ7DQo+ID4+ICsJaW50IHN0YXR1czsNCj4gPj4gKwlpbnQgcmV0ID0gMDsN
Cj4gPj4gKw0KPiA+PiArCWlmIChpcGNfZGV2LT5jZmctPm1vZGUgPT0gSVBDX0RFVl9NT0RFX0lS
USkgew0KPiA+PiArCQlpZiAoIXdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91dCgmaXBjX2Rldi0+
Y21kX2NvbXBsZXRlLA0KPiA+PiArCQkJCUlQQ19ERVZfQ01EX1RJTUVPVVQpKQ0KPiA+PiArCQkJ
cmV0ID0gLUVUSU1FRE9VVDsNCj4gPj4gKwl9IGVsc2Ugew0KPiA+PiArCQl3aGlsZSAoaXBjX2Rl
dl9zdGF0dXNfYnVzeShpcGNfZGV2KSAmJiAtLWxvb3BfY291bnQpDQo+ID4+ICsJCQl1ZGVsYXko
MSk7DQo+ID4+ICsJCWlmICghbG9vcF9jb3VudCkNCj4gPj4gKwkJCXJldCA9IC1FVElNRURPVVQ7
DQo+ID4+ICsJfQ0KPiA+PiArDQo+ID4+ICsJaWYgKHJldCA8IDApIHsNCj4gPj4gKwkJZGV2X2Vy
cigmaXBjX2Rldi0+ZGV2LA0KPiA+PiArCQkJCSJJUEMgdGltZWQgb3V0LCBDTUQ9MHgleFxuIiwg
aXBjX2Rldi0NCj4gPj4+IGNtZCk7DQo+ID4+ICsJCXJldHVybiByZXQ7DQo+ID4+ICsJfQ0KPiA+
PiArDQo+ID4+ICsJcmVnbWFwX3JlYWQoaXBjX2Rldi0+Y2ZnLT5jbWRfcmVncywgaXBjX2Rldi0+
Y2ZnLT5zdGF0dXNfcmVnLA0KPiA+PiArJnN0YXR1cyk7DQo+ID4+ICsNCj4gPj4gKwlpZiAoaXBj
X2Rldi0+b3BzLT50b19lcnJfY29kZSkNCj4gPj4gKwkJcmV0ID0gaXBjX2Rldi0+b3BzLT50b19l
cnJfY29kZShzdGF0dXMpOw0KPiA+PiArDQo+ID4+ICsJaWYgKHJldCkgew0KPiA+PiArCQlkZXZf
ZXJyKCZpcGNfZGV2LT5kZXYsDQo+ID4+ICsJCQkJIklQQyBmYWlsZWQ6ICVzLCBTVFM9MHgleCwg
Q01EPTB4JXhcbiIsDQo+ID4+ICsJCQkJaXBjX2Rldl9lcnJfc3RyaW5nKGlwY19kZXYsIHJldCks
DQo+ID4+ICsJCQkJc3RhdHVzLCBpcGNfZGV2LT5jbWQpOw0KPiA+PiArCQlyZXR1cm4gLUVJTzsN
Cj4gPj4gKwl9DQo+ID4+ICsNCj4gPj4gKwlyZXR1cm4gMDsNCj4gPj4gK30NCj4gPj4gKw0KPiA+
PiArLyoqDQo+ID4+ICsgKiBpcGNfZGV2X3NpbXBsZV9jbWQoKSAtIFNlbmQgc2ltcGxlIElQQyBj
b21tYW5kDQo+ID4+ICsgKiBAaXBjX2RldiAgICAgOiBSZWZlcmVuY2UgdG8gaXBjIGRldmljZS4N
Cj4gPj4gKyAqIEBjbWRfbGlzdCAgICA6IElQQyBjb21tYW5kIGxpc3QuDQo+ID4+ICsgKiBAY21k
bGVuICAgICAgOiBOdW1iZXIgb2YgY21kL3N1Yi1jbWRzLg0KPiA+PiArICoNCj4gPj4gKyAqIFNl
bmQgYSBzaW1wbGUgSVBDIGNvbW1hbmQgdG8gaXBjIGRldmljZS4NCj4gPj4gKyAqIFVzZSB0aGlz
IHdoZW4gZG9uJ3QgbmVlZCB0byBzcGVjaWZ5IGlucHV0L291dHB1dCBkYXRhDQo+ID4+ICsgKiBh
bmQgc291cmNlL2Rlc3QgcG9pbnRlcnMuDQo+ID4+ICsgKg0KPiA+PiArICogUmV0dXJuOglhbiBJ
UEMgZXJyb3IgY29kZSBvciAwIG9uIHN1Y2Nlc3MuDQo+ID4+ICsgKi8NCj4gPj4gKw0KPiA+PiAr
aW50IGlwY19kZXZfc2ltcGxlX2NtZChzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaXBjX2RldiwgdTMy
ICpjbWRfbGlzdCwNCj4gPj4gKwkJdTMyIGNtZGxlbikNCj4gPj4gK3sNCj4gPj4gKwlpbnQgcmV0
Ow0KPiA+PiArDQo+ID4+ICsJaWYgKCFjbWRfbGlzdCkNCj4gPj4gKwkJcmV0dXJuIC1FSU5WQUw7
DQo+ID4+ICsNCj4gPj4gKwlyZXQgPSBpcGNfZGV2X2xvY2soaXBjX2Rldik7DQo+ID4+ICsJaWYg
KHJldCkNCj4gPj4gKwkJcmV0dXJuIHJldDsNCj4gPj4gKw0KPiA+PiArCS8qIENhbGwgY3VzdG9t
IHByZS1wcm9jZXNzaW5nIGhhbmRsZXIgKi8NCj4gPj4gKwlpZiAoaXBjX2Rldi0+b3BzLT5wcmVf
c2ltcGxlX2NtZF9mbikgew0KPiA+PiArCQlyZXQgPSBpcGNfZGV2LT5vcHMtPnByZV9zaW1wbGVf
Y21kX2ZuKGNtZF9saXN0LCBjbWRsZW4pOw0KPiA+PiArCQlpZiAocmV0KQ0KPiA+PiArCQkJZ290
byB1bmxvY2tfZGV2aWNlOw0KPiA+PiArCX0NCj4gPj4gKw0KPiA+PiArCWlwY19kZXZfc2VuZF9j
bWQoaXBjX2RldiwgY21kX2xpc3RbMF0pOw0KPiA+PiArDQo+ID4+ICsJcmV0ID0gaXBjX2Rldl9j
aGVja19zdGF0dXMoaXBjX2Rldik7DQo+ID4+ICsNCj4gPj4gK3VubG9ja19kZXZpY2U6DQo+ID4+
ICsJaXBjX2Rldl91bmxvY2soaXBjX2Rldik7DQo+ID4+ICsNCj4gPj4gKwlyZXR1cm4gcmV0Ow0K
PiA+PiArfQ0KPiA+PiArRVhQT1JUX1NZTUJPTF9HUEwoaXBjX2Rldl9zaW1wbGVfY21kKTsNCj4g
Pj4gKw0KPiA+PiArLyoqDQo+ID4+ICsgKiBpcGNfZGV2X2NtZCgpIC0gU2VuZCBJUEMgY29tbWFu
ZCB3aXRoIGRhdGEuDQo+ID4+ICsgKiBAaXBjX2RldiAgICAgOiBSZWZlcmVuY2UgdG8gaXBjX2Rl
di4NCj4gPj4gKyAqIEBjbWRfbGlzdCAgICA6IEFycmF5IG9mIGNvbW1hbmRzL3N1Yi1jb21tYW5k
cy4NCj4gPj4gKyAqIEBjbWRsZW4gICAgICA6IE51bWJlciBvZiBjb21tYW5kcy4NCj4gPj4gKyAq
IEBpbiAgICAgICAgICA6IElucHV0IGRhdGEgb2YgdGhpcyBJUEMgY29tbWFuZC4NCj4gPj4gKyAq
IEBpbmxlbiAgICAgICA6IElucHV0IGRhdGEgbGVuZ3RoIGluIGR3b3Jkcy4NCj4gPj4gKyAqIEBv
dXQgICAgICAgICA6IE91dHB1dCBkYXRhIG9mIHRoaXMgSVBDIGNvbW1hbmQuDQo+ID4+ICsgKiBA
b3V0bGVuICAgICAgOiBMZW5ndGggb2Ygb3V0cHV0IGRhdGEgaW4gZHdvcmRzLg0KPiA+PiArICoN
Cj4gPj4gKyAqIFNlbmQgYW4gSVBDIGNvbW1hbmQgdG8gZGV2aWNlIHdpdGggaW5wdXQvb3V0cHV0
IGRhdGEuDQo+ID4+ICsgKg0KPiA+PiArICogUmV0dXJuOglhbiBJUEMgZXJyb3IgY29kZSBvciAw
IG9uIHN1Y2Nlc3MuDQo+ID4+ICsgKi8NCj4gPj4gK2ludCBpcGNfZGV2X2NtZChzdHJ1Y3QgaW50
ZWxfaXBjX2RldiAqaXBjX2RldiwgdTMyICpjbWRfbGlzdCwgdTMyDQo+IGNtZGxlbiwNCj4gPj4g
KwkJdTMyICppbiwgdTMyIGlubGVuLCB1MzIgKm91dCwgdTMyIG91dGxlbikgew0KPiA+PiArCWlu
dCByZXQ7DQo+ID4+ICsNCj4gPj4gKwlpZiAoIWNtZF9saXN0IHx8ICFpbikNCj4gPj4gKwkJcmV0
dXJuIC1FSU5WQUw7DQo+ID4+ICsNCj4gPj4gKwlyZXQgPSBpcGNfZGV2X2xvY2soaXBjX2Rldik7
DQo+ID4+ICsJaWYgKHJldCkNCj4gPj4gKwkJcmV0dXJuIHJldDsNCj4gPj4gKw0KPiA+PiArCS8q
IENhbGwgY3VzdG9tIHByZS1wcm9jZXNzaW5nIGhhbmRsZXIuICovDQo+ID4+ICsJaWYgKGlwY19k
ZXYtPm9wcy0+cHJlX2NtZF9mbikgew0KPiA+PiArCQlyZXQgPSBpcGNfZGV2LT5vcHMtPnByZV9j
bWRfZm4oY21kX2xpc3QsIGNtZGxlbiwgaW4sIGlubGVuLA0KPiA+PiArCQkJCW91dCwgb3V0bGVu
KTsNCj4gPj4gKwkJaWYgKHJldCkNCj4gPj4gKwkJCWdvdG8gdW5sb2NrX2RldmljZTsNCj4gPj4g
Kwl9DQo+ID4+ICsNCj4gPj4gKwkvKiBXcml0ZSBpbmxlbiBkd29yZHMgb2YgZGF0YSB0byB3cmJ1
Zl9yZWcuICovDQo+ID4+ICsJaWYgKGlubGVuID4gMCkNCj4gPj4gKwkJcmVnbWFwX2J1bGtfd3Jp
dGUoaXBjX2Rldi0+Y2ZnLT5kYXRhX3JlZ3MsDQo+ID4+ICsJCQkJaXBjX2Rldi0+Y2ZnLT53cmJ1
Zl9yZWcsIGluLCBpbmxlbik7DQo+ID4+ICsNCj4gPj4gKwlpcGNfZGV2X3NlbmRfY21kKGlwY19k
ZXYsIGNtZF9saXN0WzBdKTsNCj4gPj4gKw0KPiA+PiArCXJldCA9IGlwY19kZXZfY2hlY2tfc3Rh
dHVzKGlwY19kZXYpOw0KPiA+PiArDQo+ID4+ICsJLyogUmVhZCBvdXRsZW4gZHdvcmRzIG9mIGRh
dGEgZnJvbSByYnVnX3JlZy4gKi8NCj4gPj4gKwlpZiAoIXJldCAmJiBvdXRsZW4gPiAwKQ0KPiA+
PiArCQlyZWdtYXBfYnVsa19yZWFkKGlwY19kZXYtPmNmZy0+ZGF0YV9yZWdzLA0KPiA+PiArCQkJ
CWlwY19kZXYtPmNmZy0+cmJ1Zl9yZWcsIG91dCwgb3V0bGVuKTsNCj4gPj4gK3VubG9ja19kZXZp
Y2U6DQo+ID4+ICsJaXBjX2Rldl91bmxvY2soaXBjX2Rldik7DQo+ID4+ICsNCj4gPj4gKwlyZXR1
cm4gcmV0Ow0KPiA+PiArfQ0KPiA+PiArRVhQT1JUX1NZTUJPTF9HUEwoaXBjX2Rldl9jbWQpOw0K
PiA+PiArDQo+ID4+ICsvKioNCj4gPj4gKyAqIGlwY19kZXZfcmF3X2NtZCgpIC0gU2VuZCBJUEMg
Y29tbWFuZCB3aXRoIGRhdGEgYW5kIHBvaW50ZXJzLg0KPiA+PiArICogQGlwY19kZXYgICAgIDog
UmVmZXJlbmNlIHRvIGlwY19kZXYuDQo+ID4+ICsgKiBAY21kX2xpc3QgICAgOiBBcnJheSBvZiBj
b21tYW5kcy9zdWItY29tbWFuZHMuDQo+ID4+ICsgKiBAY21kbGVuICAgICAgOiBOdW1iZXIgb2Yg
Y29tbWFuZHMuDQo+ID4+ICsgKiBAaW4gICAgICAgICAgOiBJbnB1dCBkYXRhIG9mIHRoaXMgSVBD
IGNvbW1hbmQuDQo+ID4+ICsgKiBAaW5sZW4gICAgICAgOiBJbnB1dCBkYXRhIGxlbmd0aCBpbiBi
eXRlcy4NCj4gPj4gKyAqIEBvdXQgICAgICAgICA6IE91dHB1dCBkYXRhIG9mIHRoaXMgSVBDIGNv
bW1hbmQuDQo+ID4+ICsgKiBAb3V0bGVuICAgICAgOiBMZW5ndGggb2Ygb3V0cHV0IGRhdGEgaW4g
ZHdvcmRzLg0KPiA+PiArICogQGRwdHIgICAgICAgIDogSVBDIGRlc3RpbmF0aW9uIGRhdGEgYWRk
cmVzcy4NCj4gPj4gKyAqIEBzcHRyICAgICAgICA6IElQQyBzb3VyY2UgZGF0YSBhZGRyZXNzLg0K
PiA+PiArICoNCj4gPj4gKyAqIFNlbmQgYW4gSVBDIGNvbW1hbmQgdG8gZGV2aWNlIHdpdGggaW5w
dXQvb3V0cHV0IGRhdGEgYW5kDQo+ID4+ICsgKiBzb3VyY2UvZGVzdCBwb2ludGVycy4NCj4gPj4g
KyAqDQo+ID4+ICsgKiBSZXR1cm46CWFuIElQQyBlcnJvciBjb2RlIG9yIDAgb24gc3VjY2Vzcy4N
Cj4gPj4gKyAqLw0KPiA+IFNvcnJ5IGZvciBjb21pbmcgaW4gc28gbGF0ZSBidXQgc2luY2Ugd2Ug
YXJlIHJlZmFjdG9yaW5nIHRoZSBBUEkNCj4gPiBhbnl3YXlzLCBpc24ndCBpdCBiZXR0ZXIgdG8g
cmVkdWNlIHRoZSBzaWduYXR1cmU/IE5pbmUgcGFyYW1ldGVycyBpcw0KPiA+IGFuIGF3ZnVsIGxv
dCBhbmQgcHJvbmUgdG8gZXJyb3JzIGFuZCBkaWZmaWN1bHQgdG8gZGVidWcuIChXZSBmb3VuZCBp
dA0KPiA+IG91dCB0aGUgaGFyZCB3YXkgd2hlbiB3ZSBzdGFydGVkIHVzaW5nIHRoaXMgYSBmZXcg
eWVhcnMgYWdvLikNCj4gSSBhZ3JlZS4gSW5pdGlhbGx5IEkgdGhvdWdodCBvZiBhZGRpbmcgYSBj
b21tYW5kIHN0cnVjdHVyZSBqdXN0IGxpa2UgeW91DQo+IG1lbnRpb25lZC4gQnV0IGZpbmFsbHkg
ZGVjaWRlZCBub3QgdG8gZG8gaXQgYmVjYXVzZSwNCj4gDQo+IDEuIE5vdCBhbGwgZHJpdmVycyB1
c2VzIGFsbCBwYXJhbWV0ZXJzIG9mIHRoaXMgQVBJLiBNb3N0IG9mIHRoZW0gcGFzcw0KPiAwLDAg
Zm9yIERQVFIgYW5kIFNQVFIgcG9pbnRlcnMuIFNvIHRoZSBsYXN0IHR3byBhcmd1bWVudHMgYXJl
IGFsbW9zdCBub3QNCj4gdXNlZC4NCj4gMi4gQWRkaW5nIGEgbmV3IHN0cnVjdHVyZSByZXF1aXJl
cyBhbGwgdXNlcnMgb2YgdGhpcyBBUEkgdG8gYWRkIGJ1ZmZlciBjb2RlIC8NCj4gc29tZSBhZGRp
dGlvbmFsIGNhbGwgdG8gaW5pdGlhbGl6ZSB0aGUgY29tbWFuZCBzdHJ1Y3R1cmUgd2hpY2ggaW4g
dHVybiBtYWtlcw0KPiB0aGUgY29kZSBsb29rIGJpdCBjb21wbGV4Lg0KPiANCk9uZSBjYW4gY29t
YmluZSBTUFRSL0RQVFIgaW50byBhIHNlcGFyYXRlIHN0cnVjdCBjb3VsZCBkaXJlY3RseSBiZSBw
YXNzZWQgYXMgTlVMTCBmb3IgdXNlcnMgd2hvIGFyZSBub3QgaW50ZXJlc3RlZC4gVGhhdCB3b3Vs
ZCB0YWtlIGNhcmUgb2Ygc29tZSBjb21wbGV4aXR5Lg0KRm9yIHRoZSByZXN0IG9mIHRoZSBwYXJh
bWV0ZXJzLCByZXF1aXJpbmcgdGhlIHVzZXIgdG8gcG9wdWxhdGUgYSBzdHJ1Y3QgZm9yIGNtZF9w
YXJhbXMgY291bGQgYWN0dWFsbHkgYmUgdXNlZnVsLiBTaW5jZSB0aGUgdXNlciB3aWxsIGJlIHJl
cXVpcmVkIHRvIHNwZWNpZmljYWxseSBpbml0aWFsaXplIGVhY2ggcGFyYW1ldGVyIGJ5IG5hbWUs
IGl0IG1heSBwcmV2ZW50IGFjY2lkZW50YWwganVtYmxpbmcgb2YgcGFyYW1ldGVycyAobGlrZSBt
aXhpbmcgdXAgaW4vb3V0IGV0Yy4pIHZpcy1hLXZpcyBwYXNzaW5nIGFsbCBuaW5lIHBhcmFtZXRl
cnMgaW4gYSByb3cuDQpQbHVzIGl0IHdpbGwgYmUgYSBsb3QgYmV0dGVyIGZyb20gdGhlIHJlYWRh
YmlsaXR5IHBlcnNwZWN0aXZlLg0KU28gZnJvbSBteSBwb2ludCBvZiB2aWV3LCB0aGUgbGl0dGxl
IGNtZF9wYXJhbSBhc3NpZ25tZW50IG92ZXJoZWFkIGlzIHdvcnRoIHRoZSBiZW5lZml0cyBpbiBy
ZWFkYWJpbGl0eSArIHByZXZlbnRpbmcgYWNjaWRlbnRhbCBidWcgaW5zZXJ0aW9ucyArIGVhc2Ug
b2YgZGVidWcuDQpGV0lXIEkgaGF2ZSBub3Qgc2VlbiBzaXgrIHBhcmFtZXRlcnMgZm9yIGFueSBB
UEkgaW4gTGludXguDQoNCj4gU28gSSBhbSBub3QgcmVhbGx5IHN1cmUgd2hldGhlciBpdCBhZGQg
YW55IHZhbHVlLiBCdXQgaWYgaXRzIHRoZSByZWNvbW1lbmRlZA0KPiBhcHByb2FjaCB0aGVuIEkg
d2lsbCBtYWtlIHRoYXQgbW9kaWZpY2F0aW9uLg0KPiA+IFRoaXMgY2FuIGJlIGNvbnNvbGlkYXRl
ZCBpbnRvIGEgZmV3IG5lYXQgc3RydWN0cywgZS5nLiw6DQo+ID4gaW50IGlwY19kZXZfcmF3X2Nt
ZChzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaXBjX2Rldiwgc3RydWN0IGlwY19jbWQgKmNtZCwNCj4g
PiAJCXN0cnVjdCBpcGNfY21kX2RhdGEgKmNtZF9kYXRhLCBzdHJ1Y3QgaXBjX2RhdGFfYWRkciAq
YWRkcikNCj4gPg0KPiA+IFNhbWUgZm9yIHRoZSBpcGNfZGV2X2NtZCgpIEFQSXMgYWJvdmUgYXMg
d2VsbC4NCj4gPg0KPiA+PiArDQo+ID4+ICtpbnQgaXBjX2Rldl9yYXdfY21kKHN0cnVjdCBpbnRl
bF9pcGNfZGV2ICppcGNfZGV2LCB1MzIgKmNtZF9saXN0LA0KPiA+PiArdTMyDQo+ID4+IGNtZGxl
biwNCj4gPj4gKwkJdTggKmluLCB1MzIgaW5sZW4sIHUzMiAqb3V0LCB1MzIgb3V0bGVuLCB1MzIg
ZHB0ciwgdTMyIHNwdHIpIHsNCj4gPj4gKwlpbnQgcmV0Ow0KPiA+PiArCWludCBpbmJ1ZmxlbiA9
IERJVl9ST1VORF9VUChpbmxlbiwgNCk7DQo+ID4+ICsJdTMyICppbmJ1ZjsNCj4gPj4gKw0KPiA+
PiArCWlmICghY21kX2xpc3QgfHwgIWluKQ0KPiA+PiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gPj4g
Kw0KPiA+PiArCWluYnVmID0ga3phbGxvYyhpbmJ1ZmxlbiwgR0ZQX0tFUk5FTCk7DQo+ID4+ICsJ
aWYgKCFpbmJ1ZikNCj4gPj4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4+ICsNCj4gPj4gKwlyZXQg
PSBpcGNfZGV2X2xvY2soaXBjX2Rldik7DQo+ID4+ICsJaWYgKHJldCkNCj4gPj4gKwkJcmV0dXJu
IHJldDsNCj4gPj4gKw0KPiA+PiArCS8qIENhbGwgY3VzdG9tIHByZS1wcm9jZXNzaW5nIGhhbmRs
ZXIuICovDQo+ID4+ICsJaWYgKGlwY19kZXYtPm9wcy0+cHJlX3Jhd19jbWRfZm4pIHsNCj4gPj4g
KwkJcmV0ID0gaXBjX2Rldi0+b3BzLT5wcmVfcmF3X2NtZF9mbihjbWRfbGlzdCwgY21kbGVuLCBp
biwNCj4gPj4gaW5sZW4sDQo+ID4+ICsJCQkJb3V0LCBvdXRsZW4sIGRwdHIsIHNwdHIpOw0KPiA+
PiArCQlpZiAocmV0KQ0KPiA+PiArCQkJZ290byB1bmxvY2tfZGV2aWNlOw0KPiA+PiArCX0NCj4g
Pj4gKw0KPiA+PiArCS8qIElmIHN1cHBvcnRlZCwgd3JpdGUgRFBUUiByZWdpc3Rlci4qLw0KPiA+
PiArCWlmIChpcGNfZGV2LT5jZmctPnN1cHBvcnRfZHB0cikNCj4gPj4gKwkJcmVnbWFwX3dyaXRl
KGlwY19kZXYtPmNmZy0+Y21kX3JlZ3MsIGlwY19kZXYtPmNmZy0NCj4gPj4+IGRwdHJfcmVnLA0K
PiA+PiArCQkJCWRwdHIpOw0KPiA+PiArDQo+ID4+ICsJLyogSWYgc3VwcG9ydGVkLCB3cml0ZSBT
UFRSIHJlZ2lzdGVyLiAqLw0KPiA+PiArCWlmIChpcGNfZGV2LT5jZmctPnN1cHBvcnRfc3B0cikN
Cj4gPj4gKwkJcmVnbWFwX3dyaXRlKGlwY19kZXYtPmNmZy0+Y21kX3JlZ3MsIGlwY19kZXYtPmNm
Zy0NCj4gPj4+IHNwdHJfcmVnLA0KPiA+PiArCQkJCXNwdHIpOw0KPiA+PiArDQo+ID4+ICsJbWVt
Y3B5KGluYnVmLCBpbiwgaW5sZW4pOw0KPiA+PiArDQo+ID4+ICsJLyogV3JpdGUgaW5sZW4gZHdv
cmRzIG9mIGRhdGEgdG8gd3JidWZfcmVnLiAqLw0KPiA+PiArCWlmIChpbmxlbiA+IDApDQo+ID4+
ICsJCXJlZ21hcF9idWxrX3dyaXRlKGlwY19kZXYtPmNmZy0+ZGF0YV9yZWdzLA0KPiA+PiArCQkJ
CWlwY19kZXYtPmNmZy0+d3JidWZfcmVnLCBpbmJ1ZiwgaW5idWZsZW4pOw0KPiA+PiArDQo+ID4+
ICsJaXBjX2Rldl9zZW5kX2NtZChpcGNfZGV2LCBjbWRfbGlzdFswXSk7DQo+ID4+ICsNCj4gPj4g
KwlyZXQgPSBpcGNfZGV2X2NoZWNrX3N0YXR1cyhpcGNfZGV2KTsNCj4gPj4gKw0KPiA+PiArCS8q
IFJlYWQgb3V0bGVuIGR3b3JkcyBvZiBkYXRhIGZyb20gcmJ1Z19yZWcuICovDQo+ID4+ICsJaWYg
KCFyZXQgJiYgb3V0bGVuID4gMCkNCj4gPj4gKwkJcmVnbWFwX2J1bGtfcmVhZChpcGNfZGV2LT5j
ZmctPmRhdGFfcmVncywNCj4gPj4gKwkJCQlpcGNfZGV2LT5jZmctPnJidWZfcmVnLCBvdXQsIG91
dGxlbik7DQo+ID4+ICt1bmxvY2tfZGV2aWNlOg0KPiA+PiArCWlwY19kZXZfdW5sb2NrKGlwY19k
ZXYpOw0KPiA+PiArCWtmcmVlKGluYnVmKTsNCj4gPj4gKw0KPiA+PiArCXJldHVybiByZXQ7DQo+
ID4+ICt9DQo+ID4+ICtFWFBPUlRfU1lNQk9MX0dQTChpcGNfZGV2X3Jhd19jbWQpOw0KPiA+PiAr
DQo+ID4+ICsvKiBzeXNmcyBvcHRpb24gdG8gc2VuZCBzaW1wbGUgSVBDIGNvbW1hbmRzIGZyb20g
dXNlcnNwYWNlICovIHN0YXRpYw0KPiA+PiArc3NpemVfdCBpcGNfZGV2X2NtZF9yZWdfc3RvcmUo
c3RydWN0IGRldmljZSAqZGV2LA0KPiA+PiArCQkJCSAgICAgc3RydWN0IGRldmljZV9hdHRyaWJ1
dGUgKmF0dHIsDQo+ID4+ICsJCQkJICAgICBjb25zdCBjaGFyICpidWYsIHNpemVfdCBjb3VudCkg
ew0KPiA+PiArCXN0cnVjdCBpbnRlbF9pcGNfZGV2ICppcGNfZGV2ID0gZGV2X2dldF9kcnZkYXRh
KGRldik7DQo+ID4+ICsJdTMyIGNtZDsNCj4gPj4gKwlpbnQgcmV0Ow0KPiA+PiArDQo+ID4+ICsJ
cmV0ID0gc3NjYW5mKGJ1ZiwgIiVkIiwgJmNtZCk7DQo+ID4+ICsJaWYgKHJldCAhPSAxKSB7DQo+
ID4+ICsJCWRldl9lcnIoZGV2LCAiRXJyb3IgYXJnc1xuIik7DQo+ID4+ICsJCXJldHVybiAtRUlO
VkFMOw0KPiA+PiArCX0NCj4gPj4gKw0KPiA+PiArCXJldCA9IGlwY19kZXZfc2ltcGxlX2NtZChp
cGNfZGV2LCAmY21kLCAxKTsNCj4gPj4gKwlpZiAocmV0KSB7DQo+ID4+ICsJCWRldl9lcnIoZGV2
LCAiY29tbWFuZCAweCV4IGVycm9yIHdpdGggJWRcbiIsIGNtZCwgcmV0KTsNCj4gPj4gKwkJcmV0
dXJuIHJldDsNCj4gPj4gKwl9DQo+ID4+ICsJcmV0dXJuIChzc2l6ZV90KWNvdW50Ow0KPiA+PiAr
fQ0KPiA+PiArDQo+ID4+ICtzdGF0aWMgREVWSUNFX0FUVFIoc2VuZF9jbWQsIFNfSVdVU1IsIE5V
TEwsIGlwY19kZXZfY21kX3JlZ19zdG9yZSk7DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBzdHJ1Y3Qg
YXR0cmlidXRlICppcGNfZGV2X2F0dHJzW10gPSB7DQo+ID4+ICsJJmRldl9hdHRyX3NlbmRfY21k
LmF0dHIsDQo+ID4+ICsJTlVMTA0KPiA+PiArfTsNCj4gPj4gKw0KPiA+PiArc3RhdGljIGNvbnN0
IHN0cnVjdCBhdHRyaWJ1dGVfZ3JvdXAgaXBjX2Rldl9ncm91cCA9IHsNCj4gPj4gKwkuYXR0cnMg
PSBpcGNfZGV2X2F0dHJzLA0KPiA+PiArfTsNCj4gPj4gKw0KPiA+PiArc3RhdGljIGNvbnN0IHN0
cnVjdCBhdHRyaWJ1dGVfZ3JvdXAgKmlwY19kZXZfZ3JvdXBzW10gPSB7DQo+ID4+ICsJJmlwY19k
ZXZfZ3JvdXAsDQo+ID4+ICsJTlVMTCwNCj4gPj4gK307DQo+ID4+ICsNCj4gPj4gKy8qIElQQyBk
ZXZpY2UgSVJRIGhhbmRsZXIgKi8NCj4gPj4gK3N0YXRpYyBpcnFyZXR1cm5fdCBpcGNfZGV2X2ly
cV9oYW5kbGVyKGludCBpcnEsIHZvaWQgKmRldl9pZCkgew0KPiA+PiArCXN0cnVjdCBpbnRlbF9p
cGNfZGV2ICppcGNfZGV2ID0gKHN0cnVjdCBpbnRlbF9pcGNfZGV2ICopZGV2X2lkOw0KPiA+PiAr
DQo+ID4+ICsJaWYgKGlwY19kZXYtPm9wcy0+cHJlX2lycV9oYW5kbGVyX2ZuKQ0KPiA+PiArCQlp
cGNfZGV2LT5vcHMtPnByZV9pcnFfaGFuZGxlcl9mbihpcGNfZGV2LCBpcnEpOw0KPiA+PiArDQo+
ID4+ICsJY29tcGxldGUoJmlwY19kZXYtPmNtZF9jb21wbGV0ZSk7DQo+ID4+ICsNCj4gPj4gKwly
ZXR1cm4gSVJRX0hBTkRMRUQ7DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyB2b2lkIGRl
dm1faW50ZWxfaXBjX2Rldl9yZWxlYXNlKHN0cnVjdCBkZXZpY2UgKmRldiwgdm9pZCAqcmVzKSB7
DQo+ID4+ICsJc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYgPSAqKHN0cnVjdCBpbnRlbF9p
cGNfZGV2ICoqKXJlczsNCj4gPj4gKw0KPiA+PiArCWlmICghaXBjX2RldikNCj4gPj4gKwkJcmV0
dXJuOw0KPiA+PiArDQo+ID4+ICsJZGV2aWNlX2RlbCgmaXBjX2Rldi0+ZGV2KTsNCj4gPj4gKw0K
PiA+PiArCWtmcmVlKGlwY19kZXYpOw0KPiA+PiArfQ0KPiA+PiArDQo+ID4+ICtzdGF0aWMgaW50
IG1hdGNoX25hbWUoc3RydWN0IGRldmljZSAqZGV2LCBjb25zdCB2b2lkICpkYXRhKSB7DQo+ID4+
ICsgICAgICAgIGlmICghZGV2X25hbWUoZGV2KSkNCj4gPj4gKyAgICAgICAgICAgICAgICByZXR1
cm4gMDsNCj4gPj4gKw0KPiA+PiArICAgICAgICByZXR1cm4gIXN0cmNtcChkZXZfbmFtZShkZXYp
LCAoY2hhciAqKWRhdGEpOyB9DQo+ID4+ICsNCj4gPj4gKy8qKg0KPiA+PiArICogaW50ZWxfaXBj
X2Rldl9nZXQoKSAtIEdldCBJbnRlbCBJUEMgZGV2aWNlIGZyb20gbmFtZS4NCj4gPj4gKyAqIEBk
ZXZfbmFtZSAgICA6IE5hbWUgb2YgdGhlIElQQyBkZXZpY2UuDQo+ID4+ICsgKg0KPiA+PiArICog
UmV0dXJuICAgICAgIDogRVJSX1BUUi9OVUxMIG9yIGludGVsX2lwY19kZXYgcG9pbnRlciBvbiBz
dWNjZXNzLg0KPiA+PiArICovDQo+ID4+ICtzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaW50ZWxfaXBj
X2Rldl9nZXQoY29uc3QgY2hhciAqZGV2X25hbWUpIHsNCj4gPj4gKyAgICAgICAgc3RydWN0IGRl
dmljZSAqZGV2Ow0KPiA+PiArDQo+ID4+ICsJaWYgKCFkZXZfbmFtZSkNCj4gPj4gKwkJcmV0dXJu
IEVSUl9QVFIoLUVJTlZBTCk7DQo+ID4+ICsNCj4gPj4gKwlkZXYgPSBjbGFzc19maW5kX2Rldmlj
ZSgmaW50ZWxfaXBjX2NsYXNzLCBOVUxMLCBkZXZfbmFtZSwNCj4gPj4gbWF0Y2hfbmFtZSk7DQo+
ID4+ICsNCj4gPj4gKwlyZXR1cm4gZGV2ID8gZGV2X2dldF9kcnZkYXRhKGRldikgOiBOVUxMOyB9
DQo+ID4+ICtFWFBPUlRfU1lNQk9MX0dQTChpbnRlbF9pcGNfZGV2X2dldCk7DQo+ID4+ICsNCj4g
Pj4gK3N0YXRpYyB2b2lkIGRldm1faW50ZWxfaXBjX2Rldl9wdXQoc3RydWN0IGRldmljZSAqZGV2
LCB2b2lkICpyZXMpIHsNCj4gPj4gKwlpbnRlbF9pcGNfZGV2X3B1dCgqKHN0cnVjdCBpbnRlbF9p
cGNfZGV2ICoqKXJlcyk7IH0NCj4gPj4gKw0KPiA+PiArLyoqDQo+ID4+ICsgKiBkZXZtX2ludGVs
X2lwY19kZXZfZ2V0KCkgLSBSZXNvdXJjZSBtYW5hZ2VkIHZlcnNpb24gb2YNCj4gPj4gaW50ZWxf
aXBjX2Rldl9nZXQoKS4NCj4gPj4gKyAqIEBkZXYgICAgICAgICA6IERldmljZSBwb2ludGVyLg0K
PiA+PiArICogQGRldl9uYW1lICAgIDogTmFtZSBvZiB0aGUgSVBDIGRldmljZS4NCj4gPj4gKyAq
DQo+ID4+ICsgKiBSZXR1cm4gICAgICAgOiBFUlJfUFRSL05VTEwgb3IgaW50ZWxfaXBjX2RldiBw
b2ludGVyIG9uIHN1Y2Nlc3MuDQo+ID4+ICsgKi8NCj4gPj4gK3N0cnVjdCBpbnRlbF9pcGNfZGV2
ICpkZXZtX2ludGVsX2lwY19kZXZfZ2V0KHN0cnVjdCBkZXZpY2UgKmRldiwNCj4gPj4gKwkJCQkJ
Y29uc3QgY2hhciAqZGV2X25hbWUpDQo+ID4+ICt7DQo+ID4+ICsJc3RydWN0IGludGVsX2lwY19k
ZXYgKipwdHIsICppcGNfZGV2Ow0KPiA+PiArDQo+ID4+ICsJcHRyID0gZGV2cmVzX2FsbG9jKGRl
dm1faW50ZWxfaXBjX2Rldl9wdXQsIHNpemVvZigqcHRyKSwNCj4gPj4gR0ZQX0tFUk5FTCk7DQo+
ID4+ICsJaWYgKCFwdHIpDQo+ID4+ICsJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0pOw0KPiA+PiAr
DQo+ID4+ICsJaXBjX2RldiA9IGludGVsX2lwY19kZXZfZ2V0KGRldl9uYW1lKTsNCj4gPj4gKwlp
ZiAoIUlTX0VSUl9PUl9OVUxMKGlwY19kZXYpKSB7DQo+ID4+ICsJCSpwdHIgPSBpcGNfZGV2Ow0K
PiA+PiArCQlkZXZyZXNfYWRkKGRldiwgcHRyKTsNCj4gPj4gKwl9IGVsc2Ugew0KPiA+PiArCQlk
ZXZyZXNfZnJlZShwdHIpOw0KPiA+PiArCX0NCj4gPj4gKw0KPiA+PiArCXJldHVybiBpcGNfZGV2
Ow0KPiA+PiArfQ0KPiA+PiArRVhQT1JUX1NZTUJPTF9HUEwoZGV2bV9pbnRlbF9pcGNfZGV2X2dl
dCk7DQo+ID4+ICsNCj4gPj4gKy8qKg0KPiA+PiArICogZGV2bV9pbnRlbF9pcGNfZGV2X2NyZWF0
ZSgpIC0gQ3JlYXRlIElQQyBkZXZpY2UNCj4gPj4gKyAqIEBkZXYJCTogSVBDIHBhcmVudCBkZXZp
Y2UuDQo+ID4+ICsgKiBAZGV2bmFtZQk6IE5hbWUgb2YgdGhlIElQQyBkZXZpY2UuDQo+ID4+ICsg
KiBAY2ZnCQk6IElQQyBkZXZpY2UgY29uZmlndXJhdGlvbi4NCj4gPj4gKyAqIEBvcHMJCTogSVBD
IGRldmljZSBvcHMuDQo+ID4+ICsgKg0KPiA+PiArICogUmVzb3VyY2UgbWFuYWdlZCBBUEkgdG8g
Y3JlYXRlIElQQyBkZXZpY2Ugd2l0aA0KPiA+PiArICogZ2l2ZW4gY29uZmlndXJhdGlvbi4NCj4g
Pj4gKyAqDQo+ID4+ICsgKiBSZXR1cm4JOiBJUEMgZGV2aWNlIHBvaW50ZXIgb3IgRVJSX1BUUihl
cnJvciBjb2RlKS4NCj4gPj4gKyAqLw0KPiA+PiArc3RydWN0IGludGVsX2lwY19kZXYgKmRldm1f
aW50ZWxfaXBjX2Rldl9jcmVhdGUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+PiArCQljb25zdCBj
aGFyICpkZXZuYW1lLA0KPiA+PiArCQlzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9jZmcgKmNmZywNCj4g
Pj4gKwkJc3RydWN0IGludGVsX2lwY19kZXZfb3BzICpvcHMpDQo+ID4+ICt7DQo+ID4+ICsJc3Ry
dWN0IGludGVsX2lwY19kZXYgKipwdHIsICppcGNfZGV2Ow0KPiA+PiArCWludCByZXQ7DQo+ID4+
ICsNCj4gPj4gKwlpZiAoIWRldiAmJiAhZGV2bmFtZSAmJiAhY2ZnKQ0KPiA+PiArCQlyZXR1cm4g
RVJSX1BUUigtRUlOVkFMKTsNCj4gPj4gKw0KPiA+PiArCWlmIChpbnRlbF9pcGNfZGV2X2dldChk
ZXZuYW1lKSkgew0KPiA+PiArCQlkZXZfZXJyKGRldiwgIklQQyBkZXZpY2UgJXMgYWxyZWFkeSBl
eGlzdFxuIiwgZGV2bmFtZSk7DQo+ID4+ICsJCXJldHVybiBFUlJfUFRSKC1FSU5WQUwpOw0KPiA+
PiArCX0NCj4gPj4gKw0KPiA+PiArCXB0ciA9IGRldnJlc19hbGxvYyhkZXZtX2ludGVsX2lwY19k
ZXZfcmVsZWFzZSwgc2l6ZW9mKCpwdHIpLA0KPiA+PiArCQkJR0ZQX0tFUk5FTCk7DQo+ID4+ICsJ
aWYgKCFwdHIpDQo+ID4+ICsJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0pOw0KPiA+PiArDQo+ID4+
ICsJaXBjX2RldiA9IGt6YWxsb2Moc2l6ZW9mKCppcGNfZGV2KSwgR0ZQX0tFUk5FTCk7DQo+ID4+
ICsJaWYgKCFpcGNfZGV2KSB7DQo+ID4+ICsJCXJldCA9IC1FTk9NRU07DQo+ID4+ICsJCWdvdG8g
ZXJyX2Rldl9jcmVhdGU7DQo+ID4+ICsJfQ0KPiA+PiArDQo+ID4+ICsJaXBjX2Rldi0+ZGV2LmNs
YXNzID0gJmludGVsX2lwY19jbGFzczsNCj4gPj4gKwlpcGNfZGV2LT5kZXYucGFyZW50ID0gZGV2
Ow0KPiA+PiArCWlwY19kZXYtPmRldi5ncm91cHMgPSBpcGNfZGV2X2dyb3VwczsNCj4gPj4gKwlp
cGNfZGV2LT5jZmcgPSBjZmc7DQo+ID4+ICsJaXBjX2Rldi0+b3BzID0gb3BzOw0KPiA+PiArDQo+
ID4+ICsJbXV0ZXhfaW5pdCgmaXBjX2Rldi0+bG9jayk7DQo+ID4+ICsJaW5pdF9jb21wbGV0aW9u
KCZpcGNfZGV2LT5jbWRfY29tcGxldGUpOw0KPiA+PiArCWRldl9zZXRfZHJ2ZGF0YSgmaXBjX2Rl
di0+ZGV2LCBpcGNfZGV2KTsNCj4gPj4gKwlkZXZfc2V0X25hbWUoJmlwY19kZXYtPmRldiwgZGV2
bmFtZSk7DQo+ID4+ICsJZGV2aWNlX2luaXRpYWxpemUoJmlwY19kZXYtPmRldik7DQo+ID4+ICsN
Cj4gPj4gKwlyZXQgPSBkZXZpY2VfYWRkKCZpcGNfZGV2LT5kZXYpOw0KPiA+PiArCWlmIChyZXQg
PCAwKSB7DQo+ID4+ICsJCWRldl9lcnIoJmlwY19kZXYtPmRldiwgIiVzIGRldmljZSBjcmVhdGUg
ZmFpbGVkXG4iLA0KPiA+PiArCQkJCV9fZnVuY19fKTsNCj4gPj4gKwkJcmV0ID0gLUVOT0RFVjsN
Cj4gPj4gKwkJZ290byBlcnJfZGV2X2FkZDsNCj4gPj4gKwl9DQo+ID4+ICsNCj4gPj4gKwlpZiAo
aXBjX2Rldi0+Y2ZnLT5tb2RlID09IElQQ19ERVZfTU9ERV9JUlEpIHsNCj4gPj4gKwkJaWYgKGRl
dm1fcmVxdWVzdF9pcnEoJmlwY19kZXYtPmRldiwNCj4gPj4gKwkJCQlpcGNfZGV2LT5jZmctPmly
cSwNCj4gPj4gKwkJCQlpcGNfZGV2X2lycV9oYW5kbGVyLA0KPiA+PiArCQkJCWlwY19kZXYtPmNm
Zy0+aXJxZmxhZ3MsDQo+ID4+ICsJCQkJZGV2X25hbWUoJmlwY19kZXYtPmRldiksDQo+ID4+ICsJ
CQkJaXBjX2RldikpIHsNCj4gPj4gKwkJCWRldl9lcnIoJmlwY19kZXYtPmRldiwNCj4gPj4gKwkJ
CQkJIkZhaWxlZCB0byByZXF1ZXN0IGlycVxuIik7DQo+ID4+ICsJCQlnb3RvIGVycl9pcnFfcmVx
dWVzdDsNCj4gPj4gKwkJfQ0KPiA+PiArCX0NCj4gPj4gKw0KPiA+PiArCSpwdHIgPSBpcGNfZGV2
Ow0KPiA+PiArDQo+ID4+ICsJZGV2cmVzX2FkZChkZXYsIHB0cik7DQo+ID4+ICsNCj4gPj4gKwly
ZXR1cm4gaXBjX2RldjsNCj4gPj4gKw0KPiA+PiArZXJyX2lycV9yZXF1ZXN0Og0KPiA+PiArCWRl
dmljZV9kZWwoJmlwY19kZXYtPmRldik7DQo+ID4+ICtlcnJfZGV2X2FkZDoNCj4gPj4gKwlrZnJl
ZShpcGNfZGV2KTsNCj4gPj4gK2Vycl9kZXZfY3JlYXRlOg0KPiA+PiArCWRldnJlc19mcmVlKHB0
cik7DQo+ID4+ICsJcmV0dXJuIEVSUl9QVFIocmV0KTsNCj4gPj4gK30NCj4gPj4gK0VYUE9SVF9T
WU1CT0xfR1BMKGRldm1faW50ZWxfaXBjX2Rldl9jcmVhdGUpOw0KPiA+PiArDQo+ID4+ICtzdGF0
aWMgaW50IF9faW5pdCBpbnRlbF9pcGNfaW5pdCh2b2lkKSB7DQo+ID4+ICsJaXBjX2NoYW5uZWxf
bG9ja19pbml0KCk7DQo+ID4+ICsJcmV0dXJuIGNsYXNzX3JlZ2lzdGVyKCZpbnRlbF9pcGNfY2xh
c3MpOyB9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyB2b2lkIF9fZXhpdCBpbnRlbF9pcGNfZXhpdCh2
b2lkKSB7DQo+ID4+ICsJY2xhc3NfdW5yZWdpc3RlcigmaW50ZWxfaXBjX2NsYXNzKTsNCj4gPj4g
K30NCj4gPj4gK3N1YnN5c19pbml0Y2FsbChpbnRlbF9pcGNfaW5pdCk7DQo+ID4+ICttb2R1bGVf
ZXhpdChpbnRlbF9pcGNfZXhpdCk7DQo+ID4+ICsNCj4gPj4gK01PRFVMRV9MSUNFTlNFKCJHUEwg
djIiKTsNCj4gPj4gK01PRFVMRV9BVVRIT1IoIkt1cHB1c3dhbXkNCj4gPj4gK1NhdGh5YW5hcmF5
YW5hbjxzYXRoeWFuYXJheWFuYW4ua3VwcHVzd2FteUBsaW51eC5pbnRlbC5jb20+Iik7DQo+ID4+
ICtNT0RVTEVfREVTQ1JJUFRJT04oIkludGVsIElQQyBkZXZpY2UgY2xhc3MgZHJpdmVyIik7DQo+
ID4+IGRpZmYgLS1naXQgYS9pbmNsdWRlL2xpbnV4L3BsYXRmb3JtX2RhdGEveDg2L2ludGVsX2lw
Y19kZXYuaA0KPiA+PiBiL2luY2x1ZGUvbGludXgvcGxhdGZvcm1fZGF0YS94ODYvaW50ZWxfaXBj
X2Rldi5oDQo+ID4+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4+IGluZGV4IDAwMDAwMDAuLmVh
ZWVkYWYNCj4gPj4gLS0tIC9kZXYvbnVsbA0KPiA+PiArKysgYi9pbmNsdWRlL2xpbnV4L3BsYXRm
b3JtX2RhdGEveDg2L2ludGVsX2lwY19kZXYuaA0KPiA+PiBAQCAtMCwwICsxLDIwNiBAQA0KPiA+
PiArLyoNCj4gPj4gKyAqIEludGVsIElQQyBjbGFzcyBkZXZpY2UgaGVhZGVyIGZpbGUuDQo+ID4+
ICsgKg0KPiA+PiArICogKEMpIENvcHlyaWdodCAyMDE3IEludGVsIENvcnBvcmF0aW9uDQo+ID4+
ICsgKg0KPiA+PiArICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVk
aXN0cmlidXRlIGl0IGFuZC9vcg0KPiA+PiArICogbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBv
ZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCj4gPj4gKyAqIGFzIHB1Ymxpc2hlZCBi
eSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyB2ZXJzaW9uIDINCj4gPj4gKyAqIG9mIHRo
ZSBMaWNlbnNlLg0KPiA+PiArICoNCj4gPj4gKyAqLw0KPiA+PiArDQo+ID4+ICsjaWZuZGVmIElO
VEVMX0lQQ19ERVZfSA0KPiA+PiArI2RlZmluZSBJTlRFTF9JUENfREVWX0gNCj4gPj4gKw0KPiA+
PiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPg0KPiA+PiArI2luY2x1ZGUgPGxpbnV4L2Rldmlj
ZS5oPg0KPiA+PiArDQo+ID4+ICsvKiBJUEMgY2hhbm5lbCB0eXBlICovDQo+ID4+ICsjZGVmaW5l
IElQQ19DSEFOTkVMX0lBX1BNQyAgICAgICAgICAgICAgICAgICAgICAwDQo+ID4+ICsjZGVmaW5l
IElQQ19DSEFOTkVMX0lBX1BVTklUICAgICAgICAgICAgICAgICAgICAxDQo+ID4+ICsjZGVmaW5l
IElQQ19DSEFOTkVMX1BNQ19QVU5JVCAgICAgICAgICAgICAgICAgICAyDQo+ID4+ICsjZGVmaW5l
IElQQ19DSEFOTkVMX0lBX1NDVSAgICAgICAgICAgICAgICAgICAgICAzDQo+ID4+ICsjZGVmaW5l
IElQQ19DSEFOTkVMX01BWCAgICAgICAgICAgICAgICAgICAgICAgICA0DQo+ID4+ICsNCj4gPj4g
Ky8qIElQQyByZXR1cm4gY29kZSAqLw0KPiA+PiArI2RlZmluZSBJUENfREVWX0VSUl9OT05FCQkJ
MA0KPiA+PiArI2RlZmluZSBJUENfREVWX0VSUl9DTURfTk9UX1NVUFBPUlRFRAkJMQ0KPiA+PiAr
I2RlZmluZSBJUENfREVWX0VSUl9DTURfTk9UX1NFUlZJQ0VECQkyDQo+ID4+ICsjZGVmaW5lIElQ
Q19ERVZfRVJSX1VOQUJMRV9UT19TRVJWSUNFCQkzDQo+ID4+ICsjZGVmaW5lIElQQ19ERVZfRVJS
X0NNRF9JTlZBTElECQkJNA0KPiA+PiArI2RlZmluZSBJUENfREVWX0VSUl9DTURfRkFJTEVECQkJ
NQ0KPiA+PiArI2RlZmluZSBJUENfREVWX0VSUl9FTVNFQ1VSSVRZCQkJNg0KPiA+PiArI2RlZmlu
ZSBJUENfREVWX0VSUl9VTlNJR05FREtFUk5FTAkJNw0KPiA+PiArI2RlZmluZSBJUENfREVWX0VS
Ul9NQVgJCQkJOA0KPiA+PiArDQo+ID4+ICsvKiBJUEMgbW9kZSAqLw0KPiA+PiArI2RlZmluZSBJ
UENfREVWX01PREVfSVJRCQkJMA0KPiA+PiArI2RlZmluZSBJUENfREVWX01PREVfUE9MTElORwkJ
CTENCj4gPj4gKw0KPiA+PiArLyogSVBDIGRldiBjb25zdGFudHMgKi8NCj4gPj4gKyNkZWZpbmUg
SVBDX0RFVl9DTURfTE9PUF9DTlQJCQkzMDAwMDAwDQo+ID4+ICsjZGVmaW5lIElQQ19ERVZfQ01E
X1RJTUVPVVQJCQkzICogSFoNCj4gPj4gKyNkZWZpbmUgSVBDX0RFVl9EQVRBX0JVRkZFUl9TSVpF
CQkxNg0KPiA+PiArDQo+ID4+ICtzdHJ1Y3QgaW50ZWxfaXBjX2RldjsNCj4gPj4gK3N0cnVjdCBp
bnRlbF9pcGNfcmF3X2NtZDsNCj4gPj4gKw0KPiA+PiArLyoqDQo+ID4+ICsgKiBzdHJ1Y3QgaW50
ZWxfaXBjX2Rldl9jZmcgLSBJUEMgZGV2aWNlIGNvbmZpZyBzdHJ1Y3R1cmUuDQo+ID4+ICsgKg0K
PiA+PiArICogSVBDIGRldmljZSBkcml2ZXJzIHVzZXMgdGhlIGZvbGxvd2luZyBjb25maWcgb3B0
aW9ucyB0bw0KPiA+PiArICogcmVnaXN0ZXIgbmV3IElQQyBkZXZpY2UuDQo+ID4+ICsgKg0KPiA+
PiArICogQGNtZF9yZWdzICAgICAgICAgICAgOiBJUEMgZGV2aWNlIGNvbW1hbmQgYmFzZSByZWdt
YXAuDQo+ID4+ICsgKiBAZGF0YV9yZWdzICAgICAgICAgICA6IElQQyBkZXZpY2UgZGF0YSBiYXNl
IHJlZ21hcC4NCj4gPj4gKyAqIEB3cmJ1Zl9yZWcgICAgICAgICAgIDogSVBDIGRldmljZSBkYXRh
IHdyaXRlIHJlZ2lzdGVyIGFkZHJlc3MuDQo+ID4+ICsgKiBAcmJ1Zl9yZWcgICAgICAgICAgICA6
IElQQyBkZXZpY2UgZGF0YSByZWFkIHJlZ2lzdGVyIGFkZHJlc3MuDQo+ID4+ICsgKiBAc3B0cl9y
ZWcgICAgICAgICAgICA6IElQQyBkZXZpY2Ugc291cmNlIGRhdGEgcG9pbnRlciByZWdpc3RlciBh
ZGRyZXNzLg0KPiA+PiArICogQGRwdHJfcmVnICAgICAgICAgICAgOiBJUEMgZGV2aWNlIGRlc3Rp
bmF0aW9uIGRhdGEgcG9pbnRlciByZWdpc3Rlcg0KPiA+PiArICogICAgICAgICAgICAgICAgICAg
ICAgICBhZGRyZXNzLg0KPiA+PiArICogQHN0YXR1c19yZWcgICAgICAgICAgOiBJUEMgY29tbWFu
ZCBzdGF0dXMgcmVnaXN0ZXIgYWRkcmVzcy4NCj4gPj4gKyAqIEBjbWRfcmVnICAgICAgICAgICAg
IDogSVBDIGNvbW1hbmQgcmVnaXN0ZXIgYWRkcmVzcy4NCj4gPj4gKyAqIEBtb2RlICAgICAgICAg
ICAgICAgIDogSVJRL1BPTExJTkcgbW9kZS4NCj4gPj4gKyAqIEBpcnEgICAgICAgICAgICAgICAg
IDogSVBDIGRldmljZSBJUlEgbnVtYmVyLg0KPiA+PiArICogQGlycWZsYWdzICAgICAgICAgICAg
OiBJUEMgZGV2aWNlIElSUSBmbGFncy4NCj4gPj4gKyAqIEBjaGFuX3R5cGUgICAgICAgICAgIDog
SVBDIGRldmljZSBjaGFubmVsIHR5cGUoUE1DL1BVTklUKS4NCj4gPj4gKyAqIEBtc2kgICAgICAg
ICAgICAgICAgIDogRW5hYmxlL0Rpc2FibGUgTVNJIGZvciBJUEMgY29tbWFuZHMuDQo+ID4+ICsg
KiBAc3VwcG9ydF9kcHRyICAgICAgICA6IFN1cHBvcnQgRFBUUiB1cGRhdGUuDQo+ID4+ICsgKiBA
c3VwcG9ydF9zcHRyICAgICAgICA6IFN1cHBvcnQgU1BUUiB1cGRhdGUuDQo+ID4+ICsgKg0KPiA+
PiArICovDQo+ID4+ICtzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9jZmcgew0KPiA+PiArCXN0cnVjdCBy
ZWdtYXAgKmNtZF9yZWdzOw0KPiA+PiArCXN0cnVjdCByZWdtYXAgKmRhdGFfcmVnczsNCj4gPj4g
Kwl1bnNpZ25lZCBpbnQgd3JidWZfcmVnOw0KPiA+PiArCXVuc2lnbmVkIGludCByYnVmX3JlZzsN
Cj4gPj4gKwl1bnNpZ25lZCBpbnQgc3B0cl9yZWc7DQo+ID4+ICsJdW5zaWduZWQgaW50IGRwdHJf
cmVnOw0KPiA+PiArCXVuc2lnbmVkIGludCBzdGF0dXNfcmVnOw0KPiA+PiArCXVuc2lnbmVkIGlu
dCBjbWRfcmVnOw0KPiA+PiArCWludCBtb2RlOw0KPiA+PiArCWludCBpcnE7DQo+ID4+ICsJaW50
IGlycWZsYWdzOw0KPiA+PiArCWludCBjaGFuX3R5cGU7DQo+ID4+ICsJYm9vbCB1c2VfbXNpOw0K
PiA+PiArCWJvb2wgc3VwcG9ydF9kcHRyOw0KPiA+PiArCWJvb2wgc3VwcG9ydF9zcHRyOw0KPiA+
PiArfTsNCj4gPj4gKw0KPiA+PiArLyoqDQo+ID4+ICsgKiBzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9v
cHMgLSBJUEMgZGV2aWNlIG9wcyBzdHJ1Y3R1cmUuDQo+ID4+ICsgKg0KPiA+PiArICogQ2FsbCBi
YWNrcyBmb3IgSVBDIGRldmljZSBzcGVjaWZpYyBvcGVyYXRpb25zLg0KPiA+PiArICoNCj4gPj4g
KyAqIEB0b19lcnJfY29kZSAgICAgICAgIDogU3RhdHVzIHRvIGVycm9yIGNvZGUgY29udmVyc2lv
biBmdW5jdGlvbi4NCj4gPj4gKyAqIEBidXN5X2NoZWNrICAgICAgICAgIDogQ2hlY2sgZm9yIElQ
QyBidXN5IHN0YXR1cy4NCj4gPj4gKyAqIEBlbmFibGVfbXNpICAgICAgICAgIDogRW5hYmxlIE1T
SSBmb3IgSVBDIGNvbW1hbmRzLg0KPiA+PiArICogQHByZV9zaW1wbGVfY21kX2ZuICAgOiBDdXN0
b20gcHJlLXByb2Nlc3NpbmcgZnVuY3Rpb24gZm9yDQo+ID4+ICsgKiAgICAgICAgICAgICAgICAg
ICAgICAgIGlwY19kZXZfc2ltcGxlX2NtZCgpDQo+ID4+ICsgKiBAcHJlX2NtZF9mbiAgICAgICAg
ICA6IEN1c3RvbSBwcmUtcHJvY2Vzc2luZyBmdW5jdGlvbiBmb3INCj4gPj4gKyAqICAgICAgICAg
ICAgICAgICAgICAgICAgaXBjX2Rldl9jbWQoKQ0KPiA+PiArICogQHByZV9yYXdfY21kX2ZuICAg
ICAgOiBDdXN0b20gcHJlLXByb2Nlc3NpbmcgZnVuY3Rpb24gZm9yDQo+ID4+ICsgKiAgICAgICAg
ICAgICAgICAgICAgICAgIGlwY19kZXZfcmF3X2NtZCgpDQo+ID4+ICsgKg0KPiA+PiArICovDQo+
ID4+ICtzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9vcHMgew0KPiA+PiArCWludCAoKnRvX2Vycl9jb2Rl
KShpbnQgc3RhdHVzKTsNCj4gPj4gKwlpbnQgKCpidXN5X2NoZWNrKShpbnQgc3RhdHVzKTsNCj4g
Pj4gKwl1MzIgKCplbmFibGVfbXNpKSh1MzIgY21kKTsNCj4gPj4gKwlpbnQgKCpwcmVfc2ltcGxl
X2NtZF9mbikodTMyICpjbWRfbGlzdCwgdTMyIGNtZGxlbik7DQo+ID4+ICsJaW50ICgqcHJlX2Nt
ZF9mbikodTMyICpjbWRfbGlzdCwgdTMyIGNtZGxlbiwgdTMyICppbiwgdTMyIGlubGVuLA0KPiA+
PiArCQkJdTMyICpvdXQsIHUzMiBvdXRsZW4pOw0KPiA+PiArCWludCAoKnByZV9yYXdfY21kX2Zu
KSh1MzIgKmNtZF9saXN0LCB1MzIgY21kbGVuLCB1OCAqaW4sIHUzMiBpbmxlbiwNCj4gPj4gKwkJ
CXUzMiAqb3V0LCB1MzIgb3V0bGVuLCB1MzIgZHB0ciwgdTMyIHNwdHIpOw0KPiA+PiArCWludCAo
KnByZV9pcnFfaGFuZGxlcl9mbikoc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYsIGludCBp
cnEpOw0KPiA+PiArfTsNCj4gPj4gKw0KPiA+PiArLyoqDQo+ID4+ICsgKiBzdHJ1Y3QgaW50ZWxf
aXBjX2RldiAtIEludGVsIElQQyBkZXZpY2Ugc3RydWN0dXJlLg0KPiA+PiArICoNCj4gPj4gKyAq
IFVzZWQgd2l0aCBkZXZtX2ludGVsX2lwY19kZXZfY3JlYXRlKCkgdG8gY3JlYXRlIG5ldyBJUEMg
ZGV2aWNlLg0KPiA+PiArICoNCj4gPj4gKyAqIEBkZXYgICAgICAgICAgICAgICAgIDogSVBDIGRl
dmljZSBvYmplY3QuDQo+ID4+ICsgKiBAY21kICAgICAgICAgICAgICAgICA6IEN1cnJlbnQgSVBD
IGRldmljZSBjb21tYW5kLg0KPiA+PiArICogQGNtZF9jb21wbGV0ZSAgICAgICAgOiBDb21tYW5k
IGNvbXBsZXRpb24gb2JqZWN0Lg0KPiA+PiArICogQGxvY2sgICAgICAgICAgICAgICAgOiBMb2Nr
IHRvIHByb3RlY3QgSVBDIGRldmljZSBzdHJ1Y3R1cmUuDQo+ID4+ICsgKiBAb3BzICAgICAgICAg
ICAgICAgICA6IElQQyBkZXZpY2Ugb3BzIHBvaW50ZXIuDQo+ID4+ICsgKiBAY2ZnICAgICAgICAg
ICAgICAgICA6IElQQyBkZXZpY2UgY2ZnIHBvaW50ZXIuDQo+ID4+ICsgKg0KPiA+PiArICovDQo+
ID4+ICtzdHJ1Y3QgaW50ZWxfaXBjX2RldiB7DQo+ID4+ICsJc3RydWN0IGRldmljZSBkZXY7DQo+
ID4+ICsJaW50IGNtZDsNCj4gPj4gKwlzdHJ1Y3QgY29tcGxldGlvbiBjbWRfY29tcGxldGU7DQo+
ID4+ICsJc3RydWN0IG11dGV4IGxvY2s7DQo+ID4+ICsJc3RydWN0IGludGVsX2lwY19kZXZfb3Bz
ICpvcHM7DQo+ID4+ICsJc3RydWN0IGludGVsX2lwY19kZXZfY2ZnICpjZmc7DQo+ID4+ICt9Ow0K
PiA+PiArDQo+ID4+ICsjaWYgSVNfRU5BQkxFRChDT05GSUdfSU5URUxfSVBDX0RFVikNCj4gPj4g
Kw0KPiA+PiArLyogQVBJIHRvIGNyZWF0ZSBuZXcgSVBDIGRldmljZSAqLw0KPiA+PiArc3RydWN0
IGludGVsX2lwY19kZXYgKmRldm1faW50ZWxfaXBjX2Rldl9jcmVhdGUoc3RydWN0IGRldmljZSAq
ZGV2LA0KPiA+PiArCQljb25zdCBjaGFyICpkZXZuYW1lLCBzdHJ1Y3QgaW50ZWxfaXBjX2Rldl9j
ZmcgKmNmZywNCj4gPj4gKwkJc3RydWN0IGludGVsX2lwY19kZXZfb3BzICpvcHMpOw0KPiA+PiAr
DQo+ID4+ICtpbnQgaXBjX2Rldl9zaW1wbGVfY21kKHN0cnVjdCBpbnRlbF9pcGNfZGV2ICppcGNf
ZGV2LCB1MzIgKmNtZF9saXN0LA0KPiA+PiArCQl1MzIgY21kbGVuKTsNCj4gPj4gK2ludCBpcGNf
ZGV2X2NtZChzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaXBjX2RldiwgdTMyICpjbWRfbGlzdCwgdTMy
DQo+IGNtZGxlbiwNCj4gPj4gKwkJdTMyICppbiwgdTMyIGlubGVuLCB1MzIgKm91dCwgdTMyIG91
dGxlbik7IGludA0KPiA+PiBpcGNfZGV2X3Jhd19jbWQoc3RydWN0DQo+ID4+ICtpbnRlbF9pcGNf
ZGV2ICppcGNfZGV2LCB1MzIgKmNtZF9saXN0LCB1MzIgY21kbGVuLA0KPiA+PiArCQl1OCAqaW4s
IHUzMiBpbmxlbiwgdTMyICpvdXQsIHUzMiBvdXRsZW4sIHUzMiBkcHRyLCB1MzIgc3B0cik7DQo+
ID4+IHN0cnVjdA0KPiA+PiAraW50ZWxfaXBjX2RldiAqaW50ZWxfaXBjX2Rldl9nZXQoY29uc3Qg
Y2hhciAqZGV2X25hbWUpOyBzdHJ1Y3QNCj4gPj4gK2ludGVsX2lwY19kZXYgKmRldm1faW50ZWxf
aXBjX2Rldl9nZXQoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+PiArCQkJCQljb25zdCBjaGFyICpk
ZXZfbmFtZSk7DQo+ID4+ICtzdGF0aWMgaW5saW5lIHZvaWQgaW50ZWxfaXBjX2Rldl9wdXQoc3Ry
dWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYpIHsNCj4gPj4gKwlwdXRfZGV2aWNlKCZpcGNfZGV2
LT5kZXYpOw0KPiA+PiArfQ0KPiA+PiArI2Vsc2UNCj4gPj4gKw0KPiA+PiArc3RhdGljIGlubGlu
ZSBzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqZGV2bV9pbnRlbF9pcGNfZGV2X2NyZWF0ZSgNCj4gPj4g
KwkJc3RydWN0IGRldmljZSAqZGV2LA0KPiA+PiArCQljb25zdCBjaGFyICpkZXZuYW1lLCBzdHJ1
Y3QgaW50ZWxfaXBjX2Rldl9jZmcgKmNmZywNCj4gPj4gKwkJc3RydWN0IGludGVsX2lwY19kZXZf
b3BzICpvcHMpDQo+ID4+ICt7DQo+ID4+ICsJcmV0dXJuIC1FSU5WQUw7DQo+ID4+ICt9DQo+ID4+
ICsNCj4gPj4gK3N0YXRpYyBpbmxpbmUgaW50IGlwY19kZXZfc2ltcGxlX2NtZChzdHJ1Y3QgaW50
ZWxfaXBjX2RldiAqaXBjX2RldiwNCj4gPj4gKwkJdTMyICpjbWRfbGlzdCwgdTMyIGNtZGxlbikN
Cj4gPj4gK3sNCj4gPj4gKwlyZXR1cm4gLUVJTlZBTDsNCj4gPj4gK30NCj4gPj4gKw0KPiA+PiAr
c3RhdGljIGludCBpcGNfZGV2X2NtZChzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaXBjX2RldiwgdTMy
ICpjbWRfbGlzdCwNCj4gPj4gKwkJdTMyIGNtZGxlbiwgdTMyICppbiwgdTMyIGlubGVuLCB1MzIg
Km91dCwgdTMyIG91dGxlbikgew0KPiA+PiArCXJldHVybiAtRUlOVkFMOw0KPiA+PiArfQ0KPiA+
PiArDQo+ID4+ICtzdGF0aWMgaW5saW5lIGludCBpcGNfZGV2X3Jhd19jbWQoc3RydWN0IGludGVs
X2lwY19kZXYgKmlwY19kZXYsIHUzMg0KPiA+PiAqY21kX2xpc3QsDQo+ID4+ICsJCXUzMiBjbWRs
ZW4sIHU4ICppbiwgdTMyIGlubGVuLCB1MzIgKm91dCwgdTMyIG91dGxlbiwNCj4gPj4gKwkJdTMy
IGRwdHIsIHUzMiBzcHRyKTsNCj4gPj4gK3sNCj4gPj4gKwlyZXR1cm4gLUVJTlZBTDsNCj4gPj4g
K30NCj4gPj4gKw0KPiA+PiArc3RhdGljIGlubGluZSBzdHJ1Y3QgaW50ZWxfaXBjX2RldiAqaW50
ZWxfaXBjX2Rldl9nZXQoY29uc3QgY2hhcg0KPiA+PiArKmRldl9uYW1lKSB7DQo+ID4+ICsJcmV0
dXJuIE5VTEw7DQo+ID4+ICt9DQo+ID4+ICsNCj4gPj4gK3N0YXRpYyBpbmxpbmUgc3RydWN0IGlu
dGVsX2lwY19kZXYgKmRldm1faW50ZWxfaXBjX2Rldl9nZXQoc3RydWN0DQo+ID4+ICtkZXZpY2UN
Cj4gPj4gKmRldiwNCj4gPj4gKwkJCQkJY29uc3QgY2hhciAqZGV2X25hbWUpOw0KPiA+PiArew0K
PiA+PiArCXJldHVybiBOVUxMOw0KPiA+PiArfQ0KPiA+PiArDQo+ID4+ICtzdGF0aWMgaW5saW5l
IHZvaWQgaW50ZWxfaXBjX2Rldl9wdXQoc3RydWN0IGludGVsX2lwY19kZXYgKmlwY19kZXYpIHsN
Cj4gPj4gKwlyZXR1cm4gTlVMTDsNCj4gPj4gK30NCj4gPj4gKyNlbmRpZiAvKiBDT05GSUdfSU5U
RUxfSVBDX0RFViAqLw0KPiA+PiArI2VuZGlmIC8qIElOVEVMX0lQQ19ERVZfSCAqLw0KPiA+PiAt
LQ0KPiA+PiAyLjcuNA0KPiA+DQo+IA0KPiAtLQ0KPiBTYXRoeWFuYXJheWFuYW4gS3VwcHVzd2Ft
eQ0KPiBMaW51eCBrZXJuZWwgZGV2ZWxvcGVyDQoNCg==

^ permalink raw reply	[flat|nested] 53+ messages in thread

* RE: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
@ 2017-10-11  3:57         ` Chakravarty, Souvik K
  0 siblings, 0 replies; 53+ messages in thread
From: Chakravarty, Souvik K @ 2017-10-11  3:57 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy, a.zummo, x86, wim, mingo,
	alexandre.belloni, Zha, Qipeng, hpa, dvhart, tglx, lee.jones,
	andy
  Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86, sathyaosid

On October 11, 2017 3:39 AM, Kuppuswamy Sathyanarayanan wrote:
> Hi,
> 
> 
> On 10/08/2017 09:53 PM, Chakravarty, Souvik K wrote:
> >> From: sathyanarayanan.kuppuswamy@linux.intel.com
> >> [mailto:sathyanarayanan.kuppuswamy@linux.intel.com]
> >> Sent: Sunday, October 8, 2017 3:50 AM
> >> To: a.zummo@towertech.it; x86@kernel.org; wim@iguana.be;
> >> mingo@redhat.com; alexandre.belloni@free-electrons.com; Zha, Qipeng
> >> <qipeng.zha@intel.com>; hpa@zytor.com; dvhart@infradead.org;
> >> tglx@linutronix.de; lee.jones@linaro.org; andy@infradead.org;
> >> Chakravarty, Souvik K <souvik.k.chakravarty@intel.com>
> >> Cc: linux-rtc@vger.kernel.org; linux-watchdog@vger.kernel.org; linux-
> >> kernel@vger.kernel.org; platform-driver-x86@vger.kernel.org;
> >> sathyaosid@gmail.com; Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> Subject: [RFC v5 4/8] platform: x86: Add generic Intel IPC driver
> >>
> >> From: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >>
> >> Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
> >> redundantly implements the same IPC features and has lot of code
> >> duplication between them. This driver addresses this issue by
> >> grouping the common IPC functionalities under the same driver.
> >>
> >> Signed-off-by: Kuppuswamy Sathyanarayanan
> >> <sathyanarayanan.kuppuswamy@linux.intel.com>
> >> ---
> >>   drivers/platform/x86/Kconfig                    |   8 +
> >>   drivers/platform/x86/Makefile                   |   1 +
> >>   drivers/platform/x86/intel_ipc_dev.c            | 576
> >> ++++++++++++++++++++++++
> >>   include/linux/platform_data/x86/intel_ipc_dev.h | 206 +++++++++
> >>   4 files changed, 791 insertions(+)
> >>   create mode 100644 drivers/platform/x86/intel_ipc_dev.c
> >>   create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h
> >>
> >> Changes since v4:
> >>   * None
> >>
> >> Changes since v3:
> >>   * Fixed NULL pointer exception in intel_ipc_dev_get().
> >>   * Fixed error in check for duplicate intel_ipc_dev.
> >>   * Added custom interrupt handler support.
> >>   * Used char array for error string conversion.
> >>   * Added put dev support.
> >>   * Added devm_* variant of intel_ipc_dev_get().
> >>
> >> Changes since v2:
> >>   * Added ipc_dev_cmd API support.
> >>
> >> diff --git a/drivers/platform/x86/Kconfig
> >> b/drivers/platform/x86/Kconfig index da2d9ba..724ee696 100644
> >> --- a/drivers/platform/x86/Kconfig
> >> +++ b/drivers/platform/x86/Kconfig
> >> @@ -1153,6 +1153,14 @@ config SILEAD_DMI
> >>   	  with the OS-image for the device. This option supplies the missing
> >>   	  information. Enable this for x86 tablets with Silead touchscreens.
> >>
> >> +config INTEL_IPC_DEV
> >> +	bool "Intel IPC Device Driver"
> >> +	depends on X86_64
> >> +	---help---
> >> +	  This driver implements core features of Intel IPC device. Devices
> >> +	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
> >> +	  driver to implement IPC protocol of their respective device.
> >> +
> >>   endif # X86_PLATFORM_DEVICES
> >>
> >>   config PMC_ATOM
> >> diff --git a/drivers/platform/x86/Makefile
> >> b/drivers/platform/x86/Makefile index 2b315d0..99a1af1 100644
> >> --- a/drivers/platform/x86/Makefile
> >> +++ b/drivers/platform/x86/Makefile
> >> @@ -84,3 +84,4 @@ obj-$(CONFIG_PMC_ATOM)		+=
> >> pmc_atom.o
> >>   obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
> >>   obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
> >>   obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
> >> +obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
> >> diff --git a/drivers/platform/x86/intel_ipc_dev.c
> >> b/drivers/platform/x86/intel_ipc_dev.c
> >> new file mode 100644
> >> index 0000000..f55ddec
> >> --- /dev/null
> >> +++ b/drivers/platform/x86/intel_ipc_dev.c
> >> @@ -0,0 +1,576 @@
> >> +/*
> >> + * intel_ipc_dev.c: Intel IPC device class driver
> >> + *
> >> + * (C) Copyright 2017 Intel Corporation
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License
> >> + * as published by the Free Software Foundation; version 2
> >> + * of the License.
> >> + *
> >> + */
> >> +
> >> +#include <linux/device.h>
> >> +#include <linux/module.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/err.h>
> >> +#include <linux/export.h>
> >> +#include <linux/idr.h>
> >> +#include <linux/init.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/platform_data/x86/intel_ipc_dev.h>
> >> +#include <linux/regmap.h>
> >> +
> >> +/* mutex to sync different ipc devices in same channel */ static
> >> +struct mutex channel_lock[IPC_CHANNEL_MAX];
> >> +
> >> +static char *ipc_err_sources[] = {
> >> +	[IPC_DEV_ERR_NONE] =
> >> +		"No error",
> >> +	[IPC_DEV_ERR_CMD_NOT_SUPPORTED] =
> >> +		"Command not-supported/Invalid",
> >> +	[IPC_DEV_ERR_CMD_NOT_SERVICED] =
> >> +		"Command not-serviced/Invalid param",
> >> +	[IPC_DEV_ERR_UNABLE_TO_SERVICE] =
> >> +		"Unable-to-service/Cmd-timeout",
> >> +	[IPC_DEV_ERR_CMD_INVALID] =
> >> +		"Command-invalid/Cmd-locked",
> >> +	[IPC_DEV_ERR_CMD_FAILED] =
> >> +		"Command-failed/Invalid-VR-id",
> >> +	[IPC_DEV_ERR_EMSECURITY] =
> >> +		"Invalid Battery/VR-Error",
> >> +	[IPC_DEV_ERR_UNSIGNEDKERNEL] =
> >> +		"Unsigned kernel",
> >> +};
> >> +
> >> +static void ipc_channel_lock_init(void) {
> >> +	int i;
> >> +
> >> +	for (i = 0; i < IPC_CHANNEL_MAX; i++)
> >> +		mutex_init(&channel_lock[i]);
> >> +}
> >> +
> >> +static struct class intel_ipc_class = {
> >> +	.name = "intel_ipc",
> >> +	.owner = THIS_MODULE,
> >> +};
> >> +
> >> +static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev) {
> >> +	int chan_type;
> >> +
> >> +	if (!ipc_dev || !ipc_dev->cfg)
> >> +		return -ENODEV;
> >> +
> >> +	chan_type = ipc_dev->cfg->chan_type;
> >> +	if (chan_type > IPC_CHANNEL_MAX)
> >> +		return -EINVAL;
> >> +
> >> +	/* acquire channel lock */
> >> +	mutex_lock(&channel_lock[chan_type]);
> >> +
> >> +	/* acquire IPC device lock */
> >> +	mutex_lock(&ipc_dev->lock);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev) {
> >> +	int chan_type;
> >> +
> >> +	if (!ipc_dev || !ipc_dev->cfg)
> >> +		return -ENODEV;
> >> +
> >> +	chan_type = ipc_dev->cfg->chan_type;
> >> +	if (chan_type > IPC_CHANNEL_MAX)
> >> +		return -EINVAL;
> >> +
> >> +	/* release IPC device lock */
> >> +	mutex_unlock(&ipc_dev->lock);
> >> +
> >> +	/* release channel lock */
> >> +	mutex_unlock(&channel_lock[chan_type]);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
> >> +	int error)
> >> +{
> >> +	if (error < IPC_DEV_ERR_MAX)
> >> +		return ipc_err_sources[error];
> >> +
> >> +	return "Unknown Command";
> >> +}
> >> +
> >> +/* Helper function to send given command to IPC device */ static
> >> +inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd) {
> >> +	ipc_dev->cmd = cmd;
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
> >> +		reinit_completion(&ipc_dev->cmd_complete);
> >> +
> >> +	if (ipc_dev->ops->enable_msi)
> >> +		cmd = ipc_dev->ops->enable_msi(cmd);
> >> +
> >> +	regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);
> >> }
> >> +
> >> +static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev) {
> >> +	int status;
> >> +
> >> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> >> +&status);
> >> +
> >> +	if (ipc_dev->ops->busy_check)
> >> +		return ipc_dev->ops->busy_check(status);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/* Check the status of IPC command and return err code if failed */
> >> +static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev) {
> >> +	int loop_count = IPC_DEV_CMD_LOOP_CNT;
> >> +	int status;
> >> +	int ret = 0;
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> >> +		if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
> >> +				IPC_DEV_CMD_TIMEOUT))
> >> +			ret = -ETIMEDOUT;
> >> +	} else {
> >> +		while (ipc_dev_status_busy(ipc_dev) && --loop_count)
> >> +			udelay(1);
> >> +		if (!loop_count)
> >> +			ret = -ETIMEDOUT;
> >> +	}
> >> +
> >> +	if (ret < 0) {
> >> +		dev_err(&ipc_dev->dev,
> >> +				"IPC timed out, CMD=0x%x\n", ipc_dev-
> >>> cmd);
> >> +		return ret;
> >> +	}
> >> +
> >> +	regmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg,
> >> +&status);
> >> +
> >> +	if (ipc_dev->ops->to_err_code)
> >> +		ret = ipc_dev->ops->to_err_code(status);
> >> +
> >> +	if (ret) {
> >> +		dev_err(&ipc_dev->dev,
> >> +				"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
> >> +				ipc_dev_err_string(ipc_dev, ret),
> >> +				status, ipc_dev->cmd);
> >> +		return -EIO;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/**
> >> + * ipc_dev_simple_cmd() - Send simple IPC command
> >> + * @ipc_dev     : Reference to ipc device.
> >> + * @cmd_list    : IPC command list.
> >> + * @cmdlen      : Number of cmd/sub-cmds.
> >> + *
> >> + * Send a simple IPC command to ipc device.
> >> + * Use this when don't need to specify input/output data
> >> + * and source/dest pointers.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> >> +
> >> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen)
> >> +{
> >> +	int ret;
> >> +
> >> +	if (!cmd_list)
> >> +		return -EINVAL;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler */
> >> +	if (ipc_dev->ops->pre_simple_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
> >> +
> >> +/**
> >> + * ipc_dev_cmd() - Send IPC command with data.
> >> + * @ipc_dev     : Reference to ipc_dev.
> >> + * @cmd_list    : Array of commands/sub-commands.
> >> + * @cmdlen      : Number of commands.
> >> + * @in          : Input data of this IPC command.
> >> + * @inlen       : Input data length in dwords.
> >> + * @out         : Output data of this IPC command.
> >> + * @outlen      : Length of output data in dwords.
> >> + *
> >> + * Send an IPC command to device with input/output data.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> >> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> >> +		u32 *in, u32 inlen, u32 *out, u32 outlen) {
> >> +	int ret;
> >> +
> >> +	if (!cmd_list || !in)
> >> +		return -EINVAL;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler. */
> >> +	if (ipc_dev->ops->pre_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,
> >> +				out, outlen);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	/* Write inlen dwords of data to wrbuf_reg. */
> >> +	if (inlen > 0)
> >> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->wrbuf_reg, in, inlen);
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +	/* Read outlen dwords of data from rbug_reg. */
> >> +	if (!ret && outlen > 0)
> >> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_cmd);
> >> +
> >> +/**
> >> + * ipc_dev_raw_cmd() - Send IPC command with data and pointers.
> >> + * @ipc_dev     : Reference to ipc_dev.
> >> + * @cmd_list    : Array of commands/sub-commands.
> >> + * @cmdlen      : Number of commands.
> >> + * @in          : Input data of this IPC command.
> >> + * @inlen       : Input data length in bytes.
> >> + * @out         : Output data of this IPC command.
> >> + * @outlen      : Length of output data in dwords.
> >> + * @dptr        : IPC destination data address.
> >> + * @sptr        : IPC source data address.
> >> + *
> >> + * Send an IPC command to device with input/output data and
> >> + * source/dest pointers.
> >> + *
> >> + * Return:	an IPC error code or 0 on success.
> >> + */
> > Sorry for coming in so late but since we are refactoring the API
> > anyways, isn't it better to reduce the signature? Nine parameters is
> > an awful lot and prone to errors and difficult to debug. (We found it
> > out the hard way when we started using this a few years ago.)
> I agree. Initially I thought of adding a command structure just like you
> mentioned. But finally decided not to do it because,
> 
> 1. Not all drivers uses all parameters of this API. Most of them pass
> 0,0 for DPTR and SPTR pointers. So the last two arguments are almost not
> used.
> 2. Adding a new structure requires all users of this API to add buffer code /
> some additional call to initialize the command structure which in turn makes
> the code look bit complex.
> 
One can combine SPTR/DPTR into a separate struct could directly be passed as NULL for users who are not interested. That would take care of some complexity.
For the rest of the parameters, requiring the user to populate a struct for cmd_params could actually be useful. Since the user will be required to specifically initialize each parameter by name, it may prevent accidental jumbling of parameters (like mixing up in/out etc.) vis-a-vis passing all nine parameters in a row.
Plus it will be a lot better from the readability perspective.
So from my point of view, the little cmd_param assignment overhead is worth the benefits in readability + preventing accidental bug insertions + ease of debug.
FWIW I have not seen six+ parameters for any API in Linux.

> So I am not really sure whether it add any value. But if its the recommended
> approach then I will make that modification.
> > This can be consolidated into a few neat structs, e.g.,:
> > int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, struct ipc_cmd *cmd,
> > 		struct ipc_cmd_data *cmd_data, struct ipc_data_addr *addr)
> >
> > Same for the ipc_dev_cmd() APIs above as well.
> >
> >> +
> >> +int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +u32
> >> cmdlen,
> >> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr) {
> >> +	int ret;
> >> +	int inbuflen = DIV_ROUND_UP(inlen, 4);
> >> +	u32 *inbuf;
> >> +
> >> +	if (!cmd_list || !in)
> >> +		return -EINVAL;
> >> +
> >> +	inbuf = kzalloc(inbuflen, GFP_KERNEL);
> >> +	if (!inbuf)
> >> +		return -ENOMEM;
> >> +
> >> +	ret = ipc_dev_lock(ipc_dev);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* Call custom pre-processing handler. */
> >> +	if (ipc_dev->ops->pre_raw_cmd_fn) {
> >> +		ret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in,
> >> inlen,
> >> +				out, outlen, dptr, sptr);
> >> +		if (ret)
> >> +			goto unlock_device;
> >> +	}
> >> +
> >> +	/* If supported, write DPTR register.*/
> >> +	if (ipc_dev->cfg->support_dptr)
> >> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >>> dptr_reg,
> >> +				dptr);
> >> +
> >> +	/* If supported, write SPTR register. */
> >> +	if (ipc_dev->cfg->support_sptr)
> >> +		regmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg-
> >>> sptr_reg,
> >> +				sptr);
> >> +
> >> +	memcpy(inbuf, in, inlen);
> >> +
> >> +	/* Write inlen dwords of data to wrbuf_reg. */
> >> +	if (inlen > 0)
> >> +		regmap_bulk_write(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);
> >> +
> >> +	ipc_dev_send_cmd(ipc_dev, cmd_list[0]);
> >> +
> >> +	ret = ipc_dev_check_status(ipc_dev);
> >> +
> >> +	/* Read outlen dwords of data from rbug_reg. */
> >> +	if (!ret && outlen > 0)
> >> +		regmap_bulk_read(ipc_dev->cfg->data_regs,
> >> +				ipc_dev->cfg->rbuf_reg, out, outlen);
> >> +unlock_device:
> >> +	ipc_dev_unlock(ipc_dev);
> >> +	kfree(inbuf);
> >> +
> >> +	return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
> >> +
> >> +/* sysfs option to send simple IPC commands from userspace */ static
> >> +ssize_t ipc_dev_cmd_reg_store(struct device *dev,
> >> +				     struct device_attribute *attr,
> >> +				     const char *buf, size_t count) {
> >> +	struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
> >> +	u32 cmd;
> >> +	int ret;
> >> +
> >> +	ret = sscanf(buf, "%d", &cmd);
> >> +	if (ret != 1) {
> >> +		dev_err(dev, "Error args\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	ret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);
> >> +	if (ret) {
> >> +		dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
> >> +		return ret;
> >> +	}
> >> +	return (ssize_t)count;
> >> +}
> >> +
> >> +static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
> >> +
> >> +static struct attribute *ipc_dev_attrs[] = {
> >> +	&dev_attr_send_cmd.attr,
> >> +	NULL
> >> +};
> >> +
> >> +static const struct attribute_group ipc_dev_group = {
> >> +	.attrs = ipc_dev_attrs,
> >> +};
> >> +
> >> +static const struct attribute_group *ipc_dev_groups[] = {
> >> +	&ipc_dev_group,
> >> +	NULL,
> >> +};
> >> +
> >> +/* IPC device IRQ handler */
> >> +static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id) {
> >> +	struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
> >> +
> >> +	if (ipc_dev->ops->pre_irq_handler_fn)
> >> +		ipc_dev->ops->pre_irq_handler_fn(ipc_dev, irq);
> >> +
> >> +	complete(&ipc_dev->cmd_complete);
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static void devm_intel_ipc_dev_release(struct device *dev, void *res) {
> >> +	struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
> >> +
> >> +	if (!ipc_dev)
> >> +		return;
> >> +
> >> +	device_del(&ipc_dev->dev);
> >> +
> >> +	kfree(ipc_dev);
> >> +}
> >> +
> >> +static int match_name(struct device *dev, const void *data) {
> >> +        if (!dev_name(dev))
> >> +                return 0;
> >> +
> >> +        return !strcmp(dev_name(dev), (char *)data); }
> >> +
> >> +/**
> >> + * intel_ipc_dev_get() - Get Intel IPC device from name.
> >> + * @dev_name    : Name of the IPC device.
> >> + *
> >> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> >> + */
> >> +struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name) {
> >> +        struct device *dev;
> >> +
> >> +	if (!dev_name)
> >> +		return ERR_PTR(-EINVAL);
> >> +
> >> +	dev = class_find_device(&intel_ipc_class, NULL, dev_name,
> >> match_name);
> >> +
> >> +	return dev ? dev_get_drvdata(dev) : NULL; }
> >> +EXPORT_SYMBOL_GPL(intel_ipc_dev_get);
> >> +
> >> +static void devm_intel_ipc_dev_put(struct device *dev, void *res) {
> >> +	intel_ipc_dev_put(*(struct intel_ipc_dev **)res); }
> >> +
> >> +/**
> >> + * devm_intel_ipc_dev_get() - Resource managed version of
> >> intel_ipc_dev_get().
> >> + * @dev         : Device pointer.
> >> + * @dev_name    : Name of the IPC device.
> >> + *
> >> + * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.
> >> + */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> >> +					const char *dev_name)
> >> +{
> >> +	struct intel_ipc_dev **ptr, *ipc_dev;
> >> +
> >> +	ptr = devres_alloc(devm_intel_ipc_dev_put, sizeof(*ptr),
> >> GFP_KERNEL);
> >> +	if (!ptr)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	ipc_dev = intel_ipc_dev_get(dev_name);
> >> +	if (!IS_ERR_OR_NULL(ipc_dev)) {
> >> +		*ptr = ipc_dev;
> >> +		devres_add(dev, ptr);
> >> +	} else {
> >> +		devres_free(ptr);
> >> +	}
> >> +
> >> +	return ipc_dev;
> >> +}
> >> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_get);
> >> +
> >> +/**
> >> + * devm_intel_ipc_dev_create() - Create IPC device
> >> + * @dev		: IPC parent device.
> >> + * @devname	: Name of the IPC device.
> >> + * @cfg		: IPC device configuration.
> >> + * @ops		: IPC device ops.
> >> + *
> >> + * Resource managed API to create IPC device with
> >> + * given configuration.
> >> + *
> >> + * Return	: IPC device pointer or ERR_PTR(error code).
> >> + */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> >> +		const char *devname,
> >> +		struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops)
> >> +{
> >> +	struct intel_ipc_dev **ptr, *ipc_dev;
> >> +	int ret;
> >> +
> >> +	if (!dev && !devname && !cfg)
> >> +		return ERR_PTR(-EINVAL);
> >> +
> >> +	if (intel_ipc_dev_get(devname)) {
> >> +		dev_err(dev, "IPC device %s already exist\n", devname);
> >> +		return ERR_PTR(-EINVAL);
> >> +	}
> >> +
> >> +	ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
> >> +			GFP_KERNEL);
> >> +	if (!ptr)
> >> +		return ERR_PTR(-ENOMEM);
> >> +
> >> +	ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
> >> +	if (!ipc_dev) {
> >> +		ret = -ENOMEM;
> >> +		goto err_dev_create;
> >> +	}
> >> +
> >> +	ipc_dev->dev.class = &intel_ipc_class;
> >> +	ipc_dev->dev.parent = dev;
> >> +	ipc_dev->dev.groups = ipc_dev_groups;
> >> +	ipc_dev->cfg = cfg;
> >> +	ipc_dev->ops = ops;
> >> +
> >> +	mutex_init(&ipc_dev->lock);
> >> +	init_completion(&ipc_dev->cmd_complete);
> >> +	dev_set_drvdata(&ipc_dev->dev, ipc_dev);
> >> +	dev_set_name(&ipc_dev->dev, devname);
> >> +	device_initialize(&ipc_dev->dev);
> >> +
> >> +	ret = device_add(&ipc_dev->dev);
> >> +	if (ret < 0) {
> >> +		dev_err(&ipc_dev->dev, "%s device create failed\n",
> >> +				__func__);
> >> +		ret = -ENODEV;
> >> +		goto err_dev_add;
> >> +	}
> >> +
> >> +	if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
> >> +		if (devm_request_irq(&ipc_dev->dev,
> >> +				ipc_dev->cfg->irq,
> >> +				ipc_dev_irq_handler,
> >> +				ipc_dev->cfg->irqflags,
> >> +				dev_name(&ipc_dev->dev),
> >> +				ipc_dev)) {
> >> +			dev_err(&ipc_dev->dev,
> >> +					"Failed to request irq\n");
> >> +			goto err_irq_request;
> >> +		}
> >> +	}
> >> +
> >> +	*ptr = ipc_dev;
> >> +
> >> +	devres_add(dev, ptr);
> >> +
> >> +	return ipc_dev;
> >> +
> >> +err_irq_request:
> >> +	device_del(&ipc_dev->dev);
> >> +err_dev_add:
> >> +	kfree(ipc_dev);
> >> +err_dev_create:
> >> +	devres_free(ptr);
> >> +	return ERR_PTR(ret);
> >> +}
> >> +EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
> >> +
> >> +static int __init intel_ipc_init(void) {
> >> +	ipc_channel_lock_init();
> >> +	return class_register(&intel_ipc_class); }
> >> +
> >> +static void __exit intel_ipc_exit(void) {
> >> +	class_unregister(&intel_ipc_class);
> >> +}
> >> +subsys_initcall(intel_ipc_init);
> >> +module_exit(intel_ipc_exit);
> >> +
> >> +MODULE_LICENSE("GPL v2");
> >> +MODULE_AUTHOR("Kuppuswamy
> >> +Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
> >> +MODULE_DESCRIPTION("Intel IPC device class driver");
> >> diff --git a/include/linux/platform_data/x86/intel_ipc_dev.h
> >> b/include/linux/platform_data/x86/intel_ipc_dev.h
> >> new file mode 100644
> >> index 0000000..eaeedaf
> >> --- /dev/null
> >> +++ b/include/linux/platform_data/x86/intel_ipc_dev.h
> >> @@ -0,0 +1,206 @@
> >> +/*
> >> + * Intel IPC class device header file.
> >> + *
> >> + * (C) Copyright 2017 Intel Corporation
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License
> >> + * as published by the Free Software Foundation; version 2
> >> + * of the License.
> >> + *
> >> + */
> >> +
> >> +#ifndef INTEL_IPC_DEV_H
> >> +#define INTEL_IPC_DEV_H
> >> +
> >> +#include <linux/module.h>
> >> +#include <linux/device.h>
> >> +
> >> +/* IPC channel type */
> >> +#define IPC_CHANNEL_IA_PMC                      0
> >> +#define IPC_CHANNEL_IA_PUNIT                    1
> >> +#define IPC_CHANNEL_PMC_PUNIT                   2
> >> +#define IPC_CHANNEL_IA_SCU                      3
> >> +#define IPC_CHANNEL_MAX                         4
> >> +
> >> +/* IPC return code */
> >> +#define IPC_DEV_ERR_NONE			0
> >> +#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
> >> +#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
> >> +#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
> >> +#define IPC_DEV_ERR_CMD_INVALID			4
> >> +#define IPC_DEV_ERR_CMD_FAILED			5
> >> +#define IPC_DEV_ERR_EMSECURITY			6
> >> +#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
> >> +#define IPC_DEV_ERR_MAX				8
> >> +
> >> +/* IPC mode */
> >> +#define IPC_DEV_MODE_IRQ			0
> >> +#define IPC_DEV_MODE_POLLING			1
> >> +
> >> +/* IPC dev constants */
> >> +#define IPC_DEV_CMD_LOOP_CNT			3000000
> >> +#define IPC_DEV_CMD_TIMEOUT			3 * HZ
> >> +#define IPC_DEV_DATA_BUFFER_SIZE		16
> >> +
> >> +struct intel_ipc_dev;
> >> +struct intel_ipc_raw_cmd;
> >> +
> >> +/**
> >> + * struct intel_ipc_dev_cfg - IPC device config structure.
> >> + *
> >> + * IPC device drivers uses the following config options to
> >> + * register new IPC device.
> >> + *
> >> + * @cmd_regs            : IPC device command base regmap.
> >> + * @data_regs           : IPC device data base regmap.
> >> + * @wrbuf_reg           : IPC device data write register address.
> >> + * @rbuf_reg            : IPC device data read register address.
> >> + * @sptr_reg            : IPC device source data pointer register address.
> >> + * @dptr_reg            : IPC device destination data pointer register
> >> + *                        address.
> >> + * @status_reg          : IPC command status register address.
> >> + * @cmd_reg             : IPC command register address.
> >> + * @mode                : IRQ/POLLING mode.
> >> + * @irq                 : IPC device IRQ number.
> >> + * @irqflags            : IPC device IRQ flags.
> >> + * @chan_type           : IPC device channel type(PMC/PUNIT).
> >> + * @msi                 : Enable/Disable MSI for IPC commands.
> >> + * @support_dptr        : Support DPTR update.
> >> + * @support_sptr        : Support SPTR update.
> >> + *
> >> + */
> >> +struct intel_ipc_dev_cfg {
> >> +	struct regmap *cmd_regs;
> >> +	struct regmap *data_regs;
> >> +	unsigned int wrbuf_reg;
> >> +	unsigned int rbuf_reg;
> >> +	unsigned int sptr_reg;
> >> +	unsigned int dptr_reg;
> >> +	unsigned int status_reg;
> >> +	unsigned int cmd_reg;
> >> +	int mode;
> >> +	int irq;
> >> +	int irqflags;
> >> +	int chan_type;
> >> +	bool use_msi;
> >> +	bool support_dptr;
> >> +	bool support_sptr;
> >> +};
> >> +
> >> +/**
> >> + * struct intel_ipc_dev_ops - IPC device ops structure.
> >> + *
> >> + * Call backs for IPC device specific operations.
> >> + *
> >> + * @to_err_code         : Status to error code conversion function.
> >> + * @busy_check          : Check for IPC busy status.
> >> + * @enable_msi          : Enable MSI for IPC commands.
> >> + * @pre_simple_cmd_fn   : Custom pre-processing function for
> >> + *                        ipc_dev_simple_cmd()
> >> + * @pre_cmd_fn          : Custom pre-processing function for
> >> + *                        ipc_dev_cmd()
> >> + * @pre_raw_cmd_fn      : Custom pre-processing function for
> >> + *                        ipc_dev_raw_cmd()
> >> + *
> >> + */
> >> +struct intel_ipc_dev_ops {
> >> +	int (*to_err_code)(int status);
> >> +	int (*busy_check)(int status);
> >> +	u32 (*enable_msi)(u32 cmd);
> >> +	int (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);
> >> +	int (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> >> +			u32 *out, u32 outlen);
> >> +	int (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> >> +			u32 *out, u32 outlen, u32 dptr, u32 sptr);
> >> +	int (*pre_irq_handler_fn)(struct intel_ipc_dev *ipc_dev, int irq);
> >> +};
> >> +
> >> +/**
> >> + * struct intel_ipc_dev - Intel IPC device structure.
> >> + *
> >> + * Used with devm_intel_ipc_dev_create() to create new IPC device.
> >> + *
> >> + * @dev                 : IPC device object.
> >> + * @cmd                 : Current IPC device command.
> >> + * @cmd_complete        : Command completion object.
> >> + * @lock                : Lock to protect IPC device structure.
> >> + * @ops                 : IPC device ops pointer.
> >> + * @cfg                 : IPC device cfg pointer.
> >> + *
> >> + */
> >> +struct intel_ipc_dev {
> >> +	struct device dev;
> >> +	int cmd;
> >> +	struct completion cmd_complete;
> >> +	struct mutex lock;
> >> +	struct intel_ipc_dev_ops *ops;
> >> +	struct intel_ipc_dev_cfg *cfg;
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
> >> +
> >> +/* API to create new IPC device */
> >> +struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
> >> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops);
> >> +
> >> +int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen);
> >> +int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32
> cmdlen,
> >> +		u32 *in, u32 inlen, u32 *out, u32 outlen); int
> >> ipc_dev_raw_cmd(struct
> >> +intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,
> >> +		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
> >> struct
> >> +intel_ipc_dev *intel_ipc_dev_get(const char *dev_name); struct
> >> +intel_ipc_dev *devm_intel_ipc_dev_get(struct device *dev,
> >> +					const char *dev_name);
> >> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> >> +	put_device(&ipc_dev->dev);
> >> +}
> >> +#else
> >> +
> >> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
> >> +		struct device *dev,
> >> +		const char *devname, struct intel_ipc_dev_cfg *cfg,
> >> +		struct intel_ipc_dev_ops *ops)
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,
> >> +		u32 *cmd_list, u32 cmdlen)
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,
> >> +		u32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen) {
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32
> >> *cmd_list,
> >> +		u32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,
> >> +		u32 dptr, u32 sptr);
> >> +{
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static inline struct intel_ipc_dev *intel_ipc_dev_get(const char
> >> +*dev_name) {
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline struct intel_ipc_dev *devm_intel_ipc_dev_get(struct
> >> +device
> >> *dev,
> >> +					const char *dev_name);
> >> +{
> >> +	return NULL;
> >> +}
> >> +
> >> +static inline void intel_ipc_dev_put(struct intel_ipc_dev *ipc_dev) {
> >> +	return NULL;
> >> +}
> >> +#endif /* CONFIG_INTEL_IPC_DEV */
> >> +#endif /* INTEL_IPC_DEV_H */
> >> --
> >> 2.7.4
> >
> 
> --
> Sathyanarayanan Kuppuswamy
> Linux kernel developer

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2017-10-11  3:58 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-07 22:19 [RFC v5 0/8] PMC/PUNIT IPC driver cleanup sathyanarayanan.kuppuswamy
2017-10-07 22:19 ` sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA
2017-10-07 22:19 ` sathyanarayanan.kuppuswamy
2017-10-07 22:19 ` [RFC v5 1/8] platform/x86: intel_pmc_ipc: Use spin_lock to protect GCR updates sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-08 18:38   ` Andy Shevchenko
2017-10-08 18:38     ` Andy Shevchenko
2017-10-08 18:38     ` Andy Shevchenko
2017-10-08 18:54     ` Kuppuswamy, Sathyanarayanan
2017-10-08 18:54       ` Kuppuswamy, Sathyanarayanan
2017-10-08 18:54       ` Kuppuswamy, Sathyanarayanan
2017-10-07 22:19 ` [RFC v5 2/8] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-07 22:19 ` [RFC v5 3/8] platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-07 22:19 ` [RFC v5 4/8] platform: x86: Add generic Intel IPC driver sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy-VuQAYsv1563Yd54FQh9/CA
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-09  4:53   ` Chakravarty, Souvik K
2017-10-09  4:53     ` Chakravarty, Souvik K
2017-10-09  4:53     ` Chakravarty, Souvik K
2017-10-09  7:11     ` Christoph Hellwig
2017-10-09  7:11       ` Christoph Hellwig
2017-10-09  7:11       ` Christoph Hellwig
2017-10-10  0:27       ` sathyanarayanan kuppuswamy
2017-10-10  0:27         ` sathyanarayanan kuppuswamy
2017-10-10  0:27         ` sathyanarayanan kuppuswamy
2017-10-10  6:27         ` Christoph Hellwig
2017-10-10  6:27           ` Christoph Hellwig
2017-10-10  6:27           ` Christoph Hellwig
2017-10-10 22:09     ` sathyanarayanan kuppuswamy
2017-10-10 22:09       ` sathyanarayanan kuppuswamy
2017-10-11  3:57       ` Chakravarty, Souvik K
2017-10-11  3:57         ` Chakravarty, Souvik K
2017-10-11  3:57         ` Chakravarty, Souvik K
2017-10-07 22:19 ` [RFC v5 5/8] platform/x86: intel_punit_ipc: Fix resource ioremap warning sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-07 22:19 ` [RFC v5 6/8] platform/x86: intel_punit_ipc: Use generic intel ipc device calls sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-09  5:07   ` Chakravarty, Souvik K
2017-10-09  5:07     ` Chakravarty, Souvik K
2017-10-10 22:28     ` sathyanarayanan kuppuswamy
2017-10-10 22:28       ` sathyanarayanan kuppuswamy
2017-10-11  3:32       ` Chakravarty, Souvik K
2017-10-11  3:32         ` Chakravarty, Souvik K
2017-10-11  3:32         ` Chakravarty, Souvik K
2017-10-07 22:19 ` [RFC v5 7/8] platform/x86: intel_pmc_ipc: Use generic Intel IPC " sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy
2017-10-09  5:18   ` Chakravarty, Souvik K
2017-10-09  5:18     ` Chakravarty, Souvik K
2017-10-09  5:18     ` Chakravarty, Souvik K
2017-10-07 22:19 ` [RFC v5 8/8] platform/x86: intel_scu_ipc: " sathyanarayanan.kuppuswamy
2017-10-07 22:19   ` sathyanarayanan.kuppuswamy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.