linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers
@ 2020-02-17 13:14 Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 01/19] platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver Mika Westerberg
                   ` (18 more replies)
  0 siblings, 19 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Hi,

Currently both intel_scu_ipc.c and intel_pmc_ipc.c implement the same SCU
IPC communications with minor differences. This duplication does not make
much sense so this series reworks the two drivers so that there is only a
single implementation of the SCU IPC. In addition to that the API will be
updated to take SCU instance pointer as an argument, and most of the
callers will be converted to this new API. The old API is left there but
the plan is to get rid the callers and then the old API as well (this is
something we are working with Andy Shevchenko).

The intel_pmc_ipc.c is then moved under MFD which suits better for this
kind of a driver that pretty much sets up the SCU IPC and then creates a
bunch of platform devices for the things sitting behind the PMC. The driver
is renamed to intel_pmc_bxt.c which should follow the existing conventions
under drivers/mfd (and it is only meant for Intel Broxton derivatives).

This is on top of platform-driver-x86.git/for-next branch because there is
already some cleanup work queued that re-organizes Kconfig and Makefile
entries.

I have tested this on Intel Joule (Broxton-M) board.

Previous versions can be found:

  v5: https://www.spinics.net/lists/platform-driver-x86/msg20841.html
  v4: https://www.spinics.net/lists/platform-driver-x86/msg20658.html
  v3: https://www.spinics.net/lists/platform-driver-x86/msg20583.html
  v2: https://www.spinics.net/lists/platform-driver-x86/msg20446.html
  v1: https://www.spinics.net/lists/platform-driver-x86/msg20359.html

Changes from v5:

  * Order config symbols the same in both Kconfig and Makefile
  * Move error log outside of critical section, keep in one line
  * Move legacy API functions into legacy header in a separate patch (this
  * is new patch 3/19).
  * Rename devres -> dr and put kzalloc into single line
  * Capitalize words used in abbreviation in kernel-doc comments of struct
    intel_soc_pmic.
  * Use DEVICE_ATTR_WO() in MFD driver attributes
  * Put MAINTAINERS entries into correct place

Changes from v4:

  * Cleanups already merged in v5.6-rc1 reducing this series to 18 patches.
  * Make SCU IPC a simple class, and now intel_scu_ipc_register() creates
    a new device that belongs to the SCU IPC class under the parent.
  * Handle refcounting using the newly created device.
  * We still call try_module_get() in intel_scu_ipc_dev_get() because we
    need to make sure the SCU IPC provider module is not unloaded but the
    SCU IPC device refcount is now increased and decreased as well.
  * Make SCU IPC code to log an error if there is a failure so that callers
    don't need to do that.
  * Replace telemetry_pltconfig_valid() with telemetry_get_pltdata().
  * Move intel_pmc_gcr_update() closer to intel_pmc_gcr_read64().
  * Use more standard "update" pattern in intel_pmc_gcr_update() and move
    check outside of the lock.
  * Use platform_get_irq_optional() instead.
  * Move iTCO resource extraction into separate helper function
    (intel_pmc_get_tco_resources()).

Changes from v3:

  * Rename intel_scu_ipc_probe() to intel_scu_ipc_register() and _remove()
    to _unregister() accordingly.
  * Make intel_scu_ipc_register() to perform handle resource request and
    ioremap itself.
  * Add devm_intel_scu_ipc_register().
  * Improve kernel-docs of struct intel_soc_pmic.
  * Add Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt to document the
    two sysfs attributes the driver exposes.
  * Fix typos in the MFD driver
  * Drop gcr_data_readq() wrapper
  * No need to check for !pmcdev.gcr_mem_base in the MFD accessors
  * Allocate PMC instance dynamically and pass this from the callers
    (telemetry) as well
  * Take the lock in intel_pmc_s0ix_counter_read() to be consistent with other
    register accessors (and serialize them).
  * Use kstrtoul() return value directly (new patch)
  * Use static const MFD cell and resources where possible. Take a copy of
    these before they get populated and passed to the MFD code.
  * Use module_platform_driver() in the MFD driver
  * Drop dev_dbg() prints.
  * Return -EINVAL instead of -ENXIO when platform_get_resource() for
    mandatory resources.
  * Clarify "residency" in intel_pmc_s0ix_counter_read().

Changes from v2:

  * Added review tags from Andy
  * Patch 12: Put intel_scu_ipc_probe() prototype and implementation in one line
  * Patch 12: Correct wording in Kconfig description.
  * Patch 12: Put devm_request_irq() in one line.
  * Patch 14: Add blank line before intel_scu_ipc_dev_update() in header.
  * Patch 14: intel_scu_ipc_dev_update() move 'u8 data' to the next line in header.
  * Patch 14: Drop outlen check in intel_scu_ipc_dev_command_with_size().
  * Patch 16: Added Guenter's ack.
  * Patch 25: Put intel_scu_ipc_dev_command() call in one line.
  * Patch 25: Put intel_scu_ipc_dev_simple_command() call in one line.
  * Patch 32: Drop X86_INTEL_MID dependency from INTEL_SCU_PCI.
  * Patch 34: Split MFD_INTEL_PMC_BXT dependencies one per line.
  * Patch 35: Reorder to happen before patch 34.
  * Patch 35: Drop comma from terminating line.

Changes from v1:

  * Update changelog of patch 16 according to what the patch actually does.
  * Add kernel-doc for struct intel_soc_pmic.
  * Move octal permission patch to be before MFD conversion.
  * Convert the intel_pmc_bxt.c to MFD APIs whilst it is being moved under
    drivers/mfd.

Mika Westerberg (19):
  platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver
  platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails
  platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header
  platform/x86: intel_scu_ipc: Introduce new SCU IPC API
  platform/x86: intel_mid_powerbtn: Convert to use new SCU IPC API
  watchdog: intel-mid_wdt: Convert to use new SCU IPC API
  platform/x86: intel_scu_ipcutil: Convert to use new SCU IPC API
  platform/x86: intel_scu_ipc: Add managed function to register SCU IPC
  platform/x86: intel_pmc_ipc: Start using SCU IPC
  mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic
  mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API
  mfd: intel_soc_pmic_mrfld: Convert to use new SCU IPC API
  platform/x86: intel_telemetry: Convert to use new SCU IPC API
  platform/x86: intel_pmc_ipc: Drop intel_pmc_ipc_command()
  x86/platform/intel-mid: Add empty stubs for intel_scu_devices_[create|destroy]()
  platform/x86: intel_pmc_ipc: Move PCI IDs to intel_scu_pcidrv.c
  platform/x86: intel_telemetry: Add telemetry_get_pltdata()
  platform/x86: intel_pmc_ipc: Convert to MFD
  MAINTAINERS: Update entry for Intel Broxton PMC driver

 .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
 MAINTAINERS                                   |  23 +-
 arch/x86/Kconfig                              |   2 +-
 arch/x86/include/asm/intel-mid.h              |   9 +-
 arch/x86/include/asm/intel_pmc_ipc.h          |  59 --
 arch/x86/include/asm/intel_scu_ipc.h          | 114 ++-
 arch/x86/include/asm/intel_scu_ipc_legacy.h   |  91 ++
 arch/x86/include/asm/intel_telemetry.h        |   6 +-
 drivers/mfd/Kconfig                           |  20 +-
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++
 drivers/mfd/intel_soc_pmic_bxtwc.c            |  34 +-
 drivers/mfd/intel_soc_pmic_mrfld.c            |  10 +-
 drivers/platform/x86/Kconfig                  |  46 +-
 drivers/platform/x86/Makefile                 |   2 +-
 drivers/platform/x86/intel_mid_powerbtn.c     |  15 +-
 drivers/platform/x86/intel_pmc_ipc.c          | 949 ------------------
 drivers/platform/x86/intel_scu_ipc.c          | 447 +++++++--
 drivers/platform/x86/intel_scu_ipcutil.c      |  43 +-
 drivers/platform/x86/intel_scu_pcidrv.c       |  68 ++
 drivers/platform/x86/intel_telemetry_core.c   |  17 +-
 .../platform/x86/intel_telemetry_debugfs.c    |  15 +-
 drivers/platform/x86/intel_telemetry_pltdrv.c |  97 +-
 drivers/usb/typec/tcpm/Kconfig                |   2 +-
 drivers/watchdog/intel-mid_wdt.c              |  53 +-
 include/linux/mfd/intel_pmc_bxt.h             |  21 +
 include/linux/mfd/intel_soc_pmic.h            |  15 +
 27 files changed, 1365 insertions(+), 1305 deletions(-)
 create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
 delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
 create mode 100644 arch/x86/include/asm/intel_scu_ipc_legacy.h
 create mode 100644 drivers/mfd/intel_pmc_bxt.c
 delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
 create mode 100644 drivers/platform/x86/intel_scu_pcidrv.c
 create mode 100644 include/linux/mfd/intel_pmc_bxt.h

-- 
2.25.0


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

* [PATCH v6 01/19] platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails Mika Westerberg
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

The SCU IPC functionality is usable outside of Intel MID devices. For
example modern Intel CPUs include the same thing but now it is called
PMC (Power Management Controller) instead of SCU. To make the IPC
available for those split the driver into core part (intel_scu_ipc.c)
and the SCU PCI driver part (intel_scu_pcidrv.c) which then calls the
former before it goes and creates rest of the SCU devices. The SCU IPC
will also register a new class that gets assigned to the device that is
created under the parent PCI device.

We also split the Kconfig symbols so that INTEL_SCU_IPC enables the SCU
IPC library and INTEL_SCU_PCI the SCU driver and convert the users
accordingly. While there remove default y from the INTEL_SCU_PCI symbol
as it is already selected by X86_INTEL_MID.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/Kconfig                        |   2 +-
 arch/x86/include/asm/intel_scu_ipc.h    |  18 +++
 drivers/mfd/Kconfig                     |   4 +-
 drivers/platform/x86/Kconfig            |  24 ++--
 drivers/platform/x86/Makefile           |   1 +
 drivers/platform/x86/intel_scu_ipc.c    | 172 ++++++++++++++++--------
 drivers/platform/x86/intel_scu_pcidrv.c |  55 ++++++++
 7 files changed, 208 insertions(+), 68 deletions(-)
 create mode 100644 drivers/platform/x86/intel_scu_pcidrv.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index beea77046f9b..66a056f221bb 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -598,7 +598,7 @@ config X86_INTEL_MID
 	select I2C
 	select DW_APB_TIMER
 	select APB_TIMER
-	select INTEL_SCU_IPC
+	select INTEL_SCU_PCI
 	select MFD_INTEL_MSIC
 	---help---
 	  Select to build a kernel capable of supporting Intel MID (Mobile
diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 2a1442ba6e78..78f939782a67 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -2,6 +2,7 @@
 #ifndef _ASM_X86_INTEL_SCU_IPC_H_
 #define  _ASM_X86_INTEL_SCU_IPC_H_
 
+#include <linux/ioport.h>
 #include <linux/notifier.h>
 
 #define IPCMSG_INDIRECT_READ	0x02
@@ -19,6 +20,23 @@
 	#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
 	#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
 
+struct device;
+struct intel_scu_ipc_dev;
+
+/**
+ * struct intel_scu_ipc_pdata - Platform data for SCU IPC
+ * @mem: Base address of SCU IPC MMIO registers
+ * @irq: The IRQ number used for SCU (optional)
+ */
+struct intel_scu_ipc_pdata {
+	struct resource mem;
+	int irq;
+};
+
+struct intel_scu_ipc_dev *
+intel_scu_ipc_register(struct device *parent,
+		       const struct intel_scu_ipc_pdata *pdata);
+
 /* Read single register */
 int intel_scu_ipc_ioread8(u16 addr, u8 *data);
 
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2b203290e7b9..20b294ef2873 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -593,7 +593,7 @@ config INTEL_SOC_PMIC_MRFLD
 	tristate "Support for Intel Merrifield Basin Cove PMIC"
 	depends on GPIOLIB
 	depends on ACPI
-	depends on INTEL_SCU_IPC
+	depends on INTEL_SCU
 	select MFD_CORE
 	select REGMAP_IRQ
 	help
@@ -625,7 +625,7 @@ config MFD_INTEL_LPSS_PCI
 
 config MFD_INTEL_MSIC
 	bool "Intel MSIC"
-	depends on INTEL_SCU_IPC
+	depends on INTEL_SCU
 	select MFD_CORE
 	help
 	  Select this option to enable access to Intel MSIC (Avatele
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 664ead09c7fb..d2835400082b 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1287,7 +1287,7 @@ config INTEL_MFLD_THERMAL
 
 config INTEL_MID_POWER_BUTTON
 	tristate "power button driver for Intel MID platforms"
-	depends on INTEL_SCU_IPC && INPUT
+	depends on INTEL_SCU && INPUT
 	help
 	  This driver handles the power button on the Intel MID platforms.
 
@@ -1334,17 +1334,25 @@ config INTEL_PUNIT_IPC
 	  which is used to bridge the communications between kernel and P-Unit.
 
 config INTEL_SCU_IPC
-	bool "Intel SCU IPC Support"
+	bool
+
+config INTEL_SCU
+	bool
+	select INTEL_SCU_IPC
+
+config INTEL_SCU_PCI
+	bool "Intel SCU PCI driver"
 	depends on X86_INTEL_MID
-	default y
-	---help---
-	  IPC is used to bridge the communications between kernel and SCU on
-	  some embedded Intel x86 platforms. This is not needed for PC-type
-	  machines.
+	select INTEL_SCU
+	help
+	  This driver is used to bridge the communications between kernel
+	  and SCU on some embedded Intel x86 platforms. It also creates
+	  devices that are connected to the SoC through the SCU. This is
+	  not needed for PC-type machines.
 
 config INTEL_SCU_IPC_UTIL
 	tristate "Intel SCU IPC utility driver"
-	depends on INTEL_SCU_IPC
+	depends on INTEL_SCU
 	---help---
 	  The IPC Util driver provides an interface with the SCU enabling
 	  low level access for debug work and updating the firmware. Say
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2ecd08783a73..a750bd8c1e81 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_INTEL_PMC_CORE)		+= intel_pmc_core.o intel_pmc_core_pltdrv.o
 obj-$(CONFIG_INTEL_PMC_IPC)		+= intel_pmc_ipc.o
 obj-$(CONFIG_INTEL_PUNIT_IPC)		+= intel_punit_ipc.o
 obj-$(CONFIG_INTEL_SCU_IPC)		+= intel_scu_ipc.o
+obj-$(CONFIG_INTEL_SCU_PCI)		+= intel_scu_pcidrv.o
 obj-$(CONFIG_INTEL_SCU_IPC_UTIL)	+= intel_scu_ipcutil.o
 obj-$(CONFIG_INTEL_TELEMETRY)		+= intel_telemetry_core.o \
 					   intel_telemetry_pltdrv.o \
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 3d7da5266136..19c2cc41fb05 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -18,11 +18,10 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/pm.h>
-#include <linux/sfi.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
 
-#include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
 /* IPC defines the following message types */
@@ -55,14 +54,13 @@
 #define IPC_IOC	          0x100		/* IPC command register IOC bit */
 
 struct intel_scu_ipc_dev {
-	struct device *dev;
+	struct device dev;
+	struct resource mem;
+	int irq;
 	void __iomem *ipc_base;
 	struct completion cmd_complete;
-	u8 irq_mode;
 };
 
-static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
-
 #define IPC_STATUS		0x04
 #define IPC_STATUS_IRQ		BIT(2)
 #define IPC_STATUS_ERR		BIT(1)
@@ -78,8 +76,14 @@ static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
 /* Timeout in jiffies */
 #define IPC_TIMEOUT		(3 * HZ)
 
+static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
 static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
 
+static struct class intel_scu_ipc_class = {
+	.name = "intel_scu_ipc",
+	.owner = THIS_MODULE,
+};
+
 /*
  * Send ipc command
  * Command Register (Write Only):
@@ -143,7 +147,7 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
 		usleep_range(50, 100);
 	} while (time_before(jiffies, end));
 
-	dev_err(scu->dev, "IPC timed out");
+	dev_err(&scu->dev, "IPC timed out");
 	return -ETIMEDOUT;
 }
 
@@ -153,7 +157,7 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 	int status;
 
 	if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
-		dev_err(scu->dev, "IPC timed out\n");
+		dev_err(&scu->dev, "IPC timed out\n");
 		return -ETIMEDOUT;
 	}
 
@@ -166,13 +170,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 
 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);
+	return scu->irq > 0 ? 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;
+	struct intel_scu_ipc_dev *scu;
 	int nc;
 	u32 offset = 0;
 	int err;
@@ -182,11 +186,11 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 	memset(cbuf, 0, sizeof(cbuf));
 
 	mutex_lock(&ipclock);
-
-	if (scu->dev == NULL) {
+	if (!ipcdev) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
+	scu = ipcdev;
 
 	for (nc = 0; nc < count; nc++, offset += 2) {
 		cbuf[offset] = addr[nc];
@@ -326,14 +330,15 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
  */
 int intel_scu_ipc_simple_command(int cmd, int sub)
 {
-	struct intel_scu_ipc_dev *scu = &ipcdev;
+	struct intel_scu_ipc_dev *scu;
 	int err;
 
 	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
+	if (!ipcdev) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
+	scu = ipcdev;
 	ipc_command(scu, sub << 12 | cmd);
 	err = intel_scu_ipc_check_status(scu);
 	mutex_unlock(&ipclock);
@@ -356,14 +361,15 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command);
 int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
 			  u32 *out, int outlen)
 {
-	struct intel_scu_ipc_dev *scu = &ipcdev;
+	struct intel_scu_ipc_dev *scu;
 	int i, err;
 
 	mutex_lock(&ipclock);
-	if (scu->dev == NULL) {
+	if (!ipcdev) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
+	scu = ipcdev;
 
 	for (i = 0; i < inlen; i++)
 		ipc_data_writel(scu, *in++, 4 * i);
@@ -399,61 +405,113 @@ static irqreturn_t ioc(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void intel_scu_ipc_release(struct device *dev)
+{
+	struct intel_scu_ipc_dev *scu;
+
+	scu = container_of(dev, struct intel_scu_ipc_dev, dev);
+	if (scu->irq > 0)
+		free_irq(scu->irq, scu);
+	iounmap(scu->ipc_base);
+	release_mem_region(scu->mem.start, resource_size(&scu->mem));
+	kfree(scu);
+}
+
 /**
- *	ipc_probe	-	probe an Intel SCU IPC
- *	@pdev: the PCI device matching
- *	@id: entry in the match table
+ * intel_scu_ipc_register() - Register SCU IPC device
+ * @parent: Parent device
+ * @pdata: Platform specific data for SCU IPC
  *
- *	Enable and install an intel SCU IPC. This appears in the PCI space
- *	but uses some hard coded addresses as well.
+ * Call this function to register SCU IPC mechanism under @parent.
+ * Returns pointer to the new SCU IPC device or ERR_PTR() in case of
+ * failure.
  */
-static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+struct intel_scu_ipc_dev *
+intel_scu_ipc_register(struct device *parent,
+		       const struct intel_scu_ipc_pdata *pdata)
 {
 	int err;
-	struct intel_scu_ipc_dev *scu = &ipcdev;
+	struct intel_scu_ipc_dev *scu;
+	void __iomem *ipc_base;
 
-	if (scu->dev)		/* We support only one SCU */
-		return -EBUSY;
+	mutex_lock(&ipclock);
+	/* We support only one IPC */
+	if (ipcdev) {
+		err = -EBUSY;
+		goto err_unlock;
+	}
 
-	err = pcim_enable_device(pdev);
-	if (err)
-		return err;
+	scu = kzalloc(sizeof(*scu), GFP_KERNEL);
+	if (!scu) {
+		err = -ENOMEM;
+		goto err_unlock;
+	}
 
-	err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
-	if (err)
-		return err;
+	scu->dev.parent = parent;
+	scu->dev.class = &intel_scu_ipc_class;
+	scu->dev.release = intel_scu_ipc_release;
+	dev_set_name(&scu->dev, "intel_scu_ipc");
 
+	if (!request_mem_region(pdata->mem.start, resource_size(&pdata->mem),
+				"intel_scu_ipc")) {
+		err = -EBUSY;
+		goto err_free;
+	}
+
+	ipc_base = ioremap(pdata->mem.start, resource_size(&pdata->mem));
+	if (!ipc_base) {
+		err = -ENOMEM;
+		goto err_release;
+	}
+
+	scu->ipc_base = ipc_base;
+	scu->mem = pdata->mem;
+	scu->irq = pdata->irq;
 	init_completion(&scu->cmd_complete);
 
-	scu->ipc_base = pcim_iomap_table(pdev)[0];
+	if (scu->irq > 0) {
+		err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
+		if (err)
+			goto err_unmap;
+	}
 
-	err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
-			       scu);
-	if (err)
-		return err;
+	/*
+	 * After this point intel_scu_ipc_release() takes care of
+	 * releasing the SCU IPC resources once refcount drops to zero.
+	 */
+	err = device_register(&scu->dev);
+	if (err) {
+		put_device(&scu->dev);
+		goto err_unlock;
+	}
 
 	/* Assign device at last */
-	scu->dev = &pdev->dev;
+	ipcdev = scu;
+	mutex_unlock(&ipclock);
 
-	intel_scu_devices_create();
+	return scu;
 
-	pci_set_drvdata(pdev, scu);
-	return 0;
+err_unmap:
+	iounmap(ipc_base);
+err_release:
+	release_mem_region(pdata->mem.start, resource_size(&pdata->mem));
+err_free:
+	kfree(scu);
+err_unlock:
+	mutex_unlock(&ipclock);
+
+	return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(intel_scu_ipc_register);
 
-static const struct pci_device_id pci_ids[] = {
-	{ PCI_VDEVICE(INTEL, 0x080e) },
-	{ PCI_VDEVICE(INTEL, 0x08ea) },
-	{ PCI_VDEVICE(INTEL, 0x11a0) },
-	{}
-};
+static int __init intel_scu_ipc_init(void)
+{
+	return class_register(&intel_scu_ipc_class);
+}
+subsys_initcall(intel_scu_ipc_init);
 
-static struct pci_driver ipc_driver = {
-	.driver = {
-		.suppress_bind_attrs = true,
-	},
-	.name = "intel_scu_ipc",
-	.id_table = pci_ids,
-	.probe = ipc_probe,
-};
-builtin_pci_driver(ipc_driver);
+static void __exit intel_scu_ipc_exit(void)
+{
+	class_unregister(&intel_scu_ipc_class);
+}
+module_exit(intel_scu_ipc_exit);
diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c
new file mode 100644
index 000000000000..52495ce14b9c
--- /dev/null
+++ b/drivers/platform/x86/intel_scu_pcidrv.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI driver for the Intel SCU.
+ *
+ * Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
+ * Authors: Sreedhara DS (sreedhara.ds@intel.com)
+ *	    Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/intel-mid.h>
+#include <asm/intel_scu_ipc.h>
+
+static int intel_scu_pci_probe(struct pci_dev *pdev,
+			       const struct pci_device_id *id)
+{
+	struct intel_scu_ipc_pdata pdata = {};
+	struct intel_scu_ipc_dev *scu;
+	int ret;
+
+	ret = pcim_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	pdata.mem = pdev->resource[0];
+	pdata.irq = pdev->irq;
+
+	scu = intel_scu_ipc_register(&pdev->dev, &pdata);
+	if (IS_ERR(scu))
+		return PTR_ERR(scu);
+
+	intel_scu_devices_create();
+	return 0;
+}
+
+static const struct pci_device_id pci_ids[] = {
+	{ PCI_VDEVICE(INTEL, 0x080e) },
+	{ PCI_VDEVICE(INTEL, 0x08ea) },
+	{ PCI_VDEVICE(INTEL, 0x11a0) },
+	{}
+};
+
+static struct pci_driver intel_scu_pci_driver = {
+	.driver = {
+		.suppress_bind_attrs = true,
+	},
+	.name = "intel_scu",
+	.id_table = pci_ids,
+	.probe = intel_scu_pci_probe,
+};
+
+builtin_pci_driver(intel_scu_pci_driver);
-- 
2.25.0


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

* [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 01/19] platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 14:08   ` Andy Shevchenko
  2020-02-17 13:14 ` [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header Mika Westerberg
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Currently we only log an error if the command times out which makes it
hard to figure out the failing command. This changes the driver to log
command and subcommand with the error code which should make debugging
easier. This also allows us to simplify the callers as they don't need
to log these errors themselves.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/platform/x86/intel_scu_ipc.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 19c2cc41fb05..7512d550b375 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -147,7 +147,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
 		usleep_range(50, 100);
 	} while (time_before(jiffies, end));
 
-	dev_err(&scu->dev, "IPC timed out");
 	return -ETIMEDOUT;
 }
 
@@ -156,10 +155,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 {
 	int status;
 
-	if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
-		dev_err(&scu->dev, "IPC timed out\n");
+	if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
 		return -ETIMEDOUT;
-	}
 
 	status = ipc_read_status(scu);
 	if (status & IPC_STATUS_ERR)
@@ -331,6 +328,7 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
 int intel_scu_ipc_simple_command(int cmd, int sub)
 {
 	struct intel_scu_ipc_dev *scu;
+	u32 cmdval;
 	int err;
 
 	mutex_lock(&ipclock);
@@ -339,9 +337,12 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
 		return -ENODEV;
 	}
 	scu = ipcdev;
-	ipc_command(scu, sub << 12 | cmd);
+	cmdval = sub << 12 | cmd;
+	ipc_command(scu, cmdval);
 	err = intel_scu_ipc_check_status(scu);
 	mutex_unlock(&ipclock);
+	if (err)
+		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
 	return err;
 }
 EXPORT_SYMBOL(intel_scu_ipc_simple_command);
@@ -362,6 +363,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
 			  u32 *out, int outlen)
 {
 	struct intel_scu_ipc_dev *scu;
+	u32 cmdval;
 	int i, err;
 
 	mutex_lock(&ipclock);
@@ -374,7 +376,8 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
 	for (i = 0; i < inlen; i++)
 		ipc_data_writel(scu, *in++, 4 * i);
 
-	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
+	cmdval = (inlen << 16) | (sub << 12) | cmd;
+	ipc_command(scu, cmdval);
 	err = intel_scu_ipc_check_status(scu);
 
 	if (!err) {
@@ -383,6 +386,8 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
 	}
 
 	mutex_unlock(&ipclock);
+	if (err)
+		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
 	return err;
 }
 EXPORT_SYMBOL(intel_scu_ipc_command);
-- 
2.25.0


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

* [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 01/19] platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 14:11   ` Andy Shevchenko
  2020-02-17 13:14 ` [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API Mika Westerberg
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

In preparation for introducing a new API for SCU IPC, move the legacy
API and constants to a separate header that is is subject to be removed
eventually.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 arch/x86/include/asm/intel_scu_ipc.h        | 56 +------------------
 arch/x86/include/asm/intel_scu_ipc_legacy.h | 62 +++++++++++++++++++++
 2 files changed, 63 insertions(+), 55 deletions(-)
 create mode 100644 arch/x86/include/asm/intel_scu_ipc_legacy.h

diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 78f939782a67..a2f4694a253c 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -3,22 +3,6 @@
 #define  _ASM_X86_INTEL_SCU_IPC_H_
 
 #include <linux/ioport.h>
-#include <linux/notifier.h>
-
-#define IPCMSG_INDIRECT_READ	0x02
-#define IPCMSG_INDIRECT_WRITE	0x05
-
-#define IPCMSG_COLD_OFF		0x80	/* Only for Tangier */
-
-#define IPCMSG_WARM_RESET	0xF0
-#define IPCMSG_COLD_RESET	0xF1
-#define IPCMSG_SOFT_RESET	0xF2
-#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 */
 
 struct device;
 struct intel_scu_ipc_dev;
@@ -37,44 +21,6 @@ struct intel_scu_ipc_dev *
 intel_scu_ipc_register(struct device *parent,
 		       const struct intel_scu_ipc_pdata *pdata);
 
-/* Read single register */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data);
-
-/* Read a vector */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
-
-/* Write single register */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data);
-
-/* Write a vector */
-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);
-
-extern struct blocking_notifier_head intel_scu_notifier;
-
-static inline void intel_scu_notifier_add(struct notifier_block *nb)
-{
-	blocking_notifier_chain_register(&intel_scu_notifier, nb);
-}
-
-static inline void intel_scu_notifier_remove(struct notifier_block *nb)
-{
-	blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
-}
-
-static inline int intel_scu_notifier_post(unsigned long v, void *p)
-{
-	return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
-}
-
-#define		SCU_AVAILABLE		1
-#define		SCU_DOWN		2
+#include <asm/intel_scu_ipc_legacy.h>
 
 #endif
diff --git a/arch/x86/include/asm/intel_scu_ipc_legacy.h b/arch/x86/include/asm/intel_scu_ipc_legacy.h
new file mode 100644
index 000000000000..d3a02bc07edd
--- /dev/null
+++ b/arch/x86/include/asm/intel_scu_ipc_legacy.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
+#define _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
+
+#include <linux/notifier.h>
+
+#define IPCMSG_INDIRECT_READ	0x02
+#define IPCMSG_INDIRECT_WRITE	0x05
+
+#define IPCMSG_COLD_OFF		0x80	/* Only for Tangier */
+
+#define IPCMSG_WARM_RESET	0xF0
+#define IPCMSG_COLD_RESET	0xF1
+#define IPCMSG_SOFT_RESET	0xF2
+#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 */
+
+/* Read single register */
+int intel_scu_ipc_ioread8(u16 addr, u8 *data);
+
+/* Read a vector */
+int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
+
+/* Write single register */
+int intel_scu_ipc_iowrite8(u16 addr, u8 data);
+
+/* Write a vector */
+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);
+
+extern struct blocking_notifier_head intel_scu_notifier;
+
+static inline void intel_scu_notifier_add(struct notifier_block *nb)
+{
+	blocking_notifier_chain_register(&intel_scu_notifier, nb);
+}
+
+static inline void intel_scu_notifier_remove(struct notifier_block *nb)
+{
+	blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
+}
+
+static inline int intel_scu_notifier_post(unsigned long v, void *p)
+{
+	return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
+}
+
+#define		SCU_AVAILABLE		1
+#define		SCU_DOWN		2
+
+#endif
-- 
2.25.0


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

* [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (2 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 14:11   ` Andy Shevchenko
  2020-02-17 13:14 ` [PATCH v6 05/19] platform/x86: intel_mid_powerbtn: Convert to use " Mika Westerberg
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

The current SCU IPC API has been operating on a single instance and
there has been no way to pin the providing module in place when the SCU
IPC is in use.

This implements a new API that takes the SCU IPC instance as first
parameter (NULL means the single instance is being used). The SCU IPC
instance can be retrieved by calling new function intel_scu_ipc_dev_get()
that take care of pinning the providing module in place as long as
intel_scu_ipc_dev_put() is not called.

The old API is updated to call the new API and is is left there in the
legacy API header to support the existing users that cannot be converted
easily.

Subsequent patches will convert most of the users over to the new API.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_scu_ipc.h        |  38 +++-
 arch/x86/include/asm/intel_scu_ipc_legacy.h |  45 +++-
 drivers/platform/x86/intel_scu_ipc.c        | 230 +++++++++++++++-----
 3 files changed, 252 insertions(+), 61 deletions(-)

diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index a2f4694a253c..27041fe999e9 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -18,8 +18,42 @@ struct intel_scu_ipc_pdata {
 };
 
 struct intel_scu_ipc_dev *
-intel_scu_ipc_register(struct device *parent,
-		       const struct intel_scu_ipc_pdata *pdata);
+__intel_scu_ipc_register(struct device *parent,
+			 const struct intel_scu_ipc_pdata *pdata,
+			 struct module *owner);
+
+#define intel_scu_ipc_register(parent, pdata)  \
+	__intel_scu_ipc_register(parent, pdata, THIS_MODULE)
+
+struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
+void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
+struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
+
+int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
+			      u8 *data);
+int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr,
+			       u8 data);
+int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr,
+			    u8 *data, size_t len);
+int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr,
+			     u8 *data, size_t len);
+
+int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
+			     u8 data, u8 mask);
+
+int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
+				     int sub);
+int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
+					int sub, const void *in, size_t inlen,
+					size_t size, void *out, size_t outlen);
+
+static inline int intel_scu_ipc_dev_command(struct intel_scu_ipc_dev *scu, int cmd,
+					    int sub, const void *in, size_t inlen,
+					    void *out, size_t outlen)
+{
+	return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
+						   inlen, out, outlen);
+}
 
 #include <asm/intel_scu_ipc_legacy.h>
 
diff --git a/arch/x86/include/asm/intel_scu_ipc_legacy.h b/arch/x86/include/asm/intel_scu_ipc_legacy.h
index d3a02bc07edd..4cf13fecb673 100644
--- a/arch/x86/include/asm/intel_scu_ipc_legacy.h
+++ b/arch/x86/include/asm/intel_scu_ipc_legacy.h
@@ -19,25 +19,54 @@
 #define IPC_CMD_VRTC_SETTIME      1	/* Set time */
 #define IPC_CMD_VRTC_SETALARM     2	/* Set alarm */
 
+/* Don't call these in new code - they will be removed eventually */
+
 /* Read single register */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data);
+static inline int intel_scu_ipc_ioread8(u16 addr, u8 *data)
+{
+	return intel_scu_ipc_dev_ioread8(NULL, addr, data);
+}
 
 /* Read a vector */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
+static inline int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
+{
+	return intel_scu_ipc_dev_readv(NULL, addr, data, len);
+}
 
 /* Write single register */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data);
+static inline int intel_scu_ipc_iowrite8(u16 addr, u8 data)
+{
+	return intel_scu_ipc_dev_iowrite8(NULL, addr, data);
+}
 
 /* Write a vector */
-int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
+static inline int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
+{
+	return intel_scu_ipc_dev_writev(NULL, addr, data, len);
+}
 
 /* Update single register based on the mask */
-int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
+static inline int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask)
+{
+	return intel_scu_ipc_dev_update(NULL, addr, data, 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);
+static inline int intel_scu_ipc_simple_command(int cmd, int sub)
+{
+	return intel_scu_ipc_dev_simple_command(NULL, cmd, sub);
+}
+
+static inline int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
+					u32 *out, int outlen)
+{
+	/* New API takes both inlen and outlen as bytes so convert here */
+	size_t inbytes = inlen * sizeof(u32);
+	size_t outbytes = outlen * sizeof(u32);
+
+	return intel_scu_ipc_dev_command_with_size(NULL, cmd, sub, in, inbytes,
+						   inlen, out, outbytes);
+}
 
 extern struct blocking_notifier_head intel_scu_notifier;
 
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 7512d550b375..a2f05b4dd2b9 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -56,6 +56,7 @@
 struct intel_scu_ipc_dev {
 	struct device dev;
 	struct resource mem;
+	struct module *owner;
 	int irq;
 	void __iomem *ipc_base;
 	struct completion cmd_complete;
@@ -84,6 +85,102 @@ static struct class intel_scu_ipc_class = {
 	.owner = THIS_MODULE,
 };
 
+/**
+ * intel_scu_ipc_dev_get() - Get SCU IPC instance
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Call intel_scu_ipc_dev_put() to release the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
+{
+	struct intel_scu_ipc_dev *scu = NULL;
+
+	mutex_lock(&ipclock);
+	if (ipcdev) {
+		get_device(&ipcdev->dev);
+		/*
+		 * Prevent the IPC provider from being unloaded while it
+		 * is being used.
+		 */
+		if (!try_module_get(ipcdev->owner))
+			put_device(&ipcdev->dev);
+		else
+			scu = ipcdev;
+	}
+
+	mutex_unlock(&ipclock);
+	return scu;
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
+
+/**
+ * intel_scu_ipc_dev_put() - Put SCU IPC instance
+ * @scu: SCU IPC instance
+ *
+ * This function releases the SCU IPC instance retrieved from
+ * intel_scu_ipc_dev_get() and allows the driver providing IPC to be
+ * unloaded.
+ */
+void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
+{
+	if (scu) {
+		module_put(scu->owner);
+		put_device(&scu->dev);
+	}
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
+
+struct intel_scu_ipc_devres {
+	struct intel_scu_ipc_dev *scu;
+};
+
+static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
+{
+	struct intel_scu_ipc_devres *dr = res;
+	struct intel_scu_ipc_dev *scu = dr->scu;
+
+	intel_scu_ipc_dev_put(scu);
+}
+
+/**
+ * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
+ * @dev: Device requesting the SCU IPC device
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
+{
+	struct intel_scu_ipc_devres *dr;
+	struct intel_scu_ipc_dev *scu;
+
+	dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
+	if (!dr)
+		return NULL;
+
+	scu = intel_scu_ipc_dev_get();
+	if (!scu) {
+		devres_free(dr);
+		return NULL;
+	}
+
+	dr->scu = scu;
+	devres_add(dev, dr);
+
+	return scu;
+}
+EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
+
 /*
  * Send ipc command
  * Command Register (Write Only):
@@ -171,9 +268,9 @@ static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *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)
+static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+			u32 count, u32 op, u32 id)
 {
-	struct intel_scu_ipc_dev *scu;
 	int nc;
 	u32 offset = 0;
 	int err;
@@ -183,11 +280,12 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 	memset(cbuf, 0, sizeof(cbuf));
 
 	mutex_lock(&ipclock);
-	if (!ipcdev) {
+	if (!scu)
+		scu = ipcdev;
+	if (!scu) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
-	scu = ipcdev;
 
 	for (nc = 0; nc < count; nc++, offset += 2) {
 		cbuf[offset] = addr[nc];
@@ -223,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 }
 
 /**
- * intel_scu_ipc_ioread8		-	read a word via the SCU
+ * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Return pointer for read byte
  *
@@ -232,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
  *
  * This function may sleep.
  */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data)
+int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
 {
-	return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+	return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_ioread8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
 
 /**
- * intel_scu_ipc_iowrite8		-	write a byte via the SCU
+ * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Byte to write
  *
@@ -248,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data)
+int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
 {
-	return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+	return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
 
 /**
- * intel_scu_ipc_readvv		-	read a set of registers
+ * intel_scu_ipc_dev_readv() - Read a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to return
  * @len: Length of array
@@ -267,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+			    size_t len)
 {
-	return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+	return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_readv);
+EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
 
 /**
- * intel_scu_ipc_writev		-	write a set of registers
+ * intel_scu_ipc_dev_writev() - Write a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to write
  * @len: Length of array
@@ -286,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+			     size_t len)
 {
-	return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+	return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_writev);
+EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
 
 /**
- * intel_scu_ipc_update_register	-	r/m/w a register
+ * intel_scu_ipc_dev_update() - Update a register
+ * @scu: Optional SCU IPC instance
  * @addr: Register address
- * @bits: Bits to update
+ * @data: Bits to update
  * @mask: Mask of bits to update
  *
  * Read-modify-write power control unit register. The first data argument
@@ -306,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
  * This function may sleep. Locking between SCU accesses is handled
  * for the caller.
  */
-int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
+int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
+			     u8 mask)
 {
-	u8 data[2] = { bits, mask };
-	return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
+	u8 tmp[2] = { data, mask };
+	return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
 }
-EXPORT_SYMBOL(intel_scu_ipc_update_register);
+EXPORT_SYMBOL(intel_scu_ipc_dev_update);
 
 /**
- * intel_scu_ipc_simple_command	-	send a simple command
+ * intel_scu_ipc_dev_simple_command() - Send a simple command
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  *
@@ -325,14 +432,16 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
  * This function may sleep. Locking for SCU accesses is handled for the
  * caller.
  */
-int intel_scu_ipc_simple_command(int cmd, int sub)
+int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
+				     int sub)
 {
-	struct intel_scu_ipc_dev *scu;
 	u32 cmdval;
 	int err;
 
 	mutex_lock(&ipclock);
-	if (!ipcdev) {
+	if (!scu)
+		scu = ipcdev;
+	if (!scu) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
@@ -345,44 +454,59 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
 		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
 	return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
 
 /**
- * intel_scu_ipc_command	-	command with data
+ * intel_scu_ipc_command_with_size() - Command with data
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  * @in: Input data
- * @inlen: Input length in dwords
+ * @inlen: Input length in bytes
+ * @size: Input size written to the IPC command register in whatever
+ *	  units (dword, byte) the particular firmware requires. Normally
+ *	  should be the same as @inlen.
  * @out: Output data
- * @outlen: Output length in dwords
+ * @outlen: Output length in bytes
  *
  * 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)
+int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
+					int sub, const void *in, size_t inlen,
+					size_t size, void *out, size_t outlen)
 {
-	struct intel_scu_ipc_dev *scu;
-	u32 cmdval;
+	size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
+	size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
+	u32 cmdval, inbuf[4] = {};
 	int i, err;
 
+	if (inbuflen > 4 || outbuflen > 4)
+		return -EINVAL;
+
 	mutex_lock(&ipclock);
-	if (!ipcdev) {
+	if (!scu)
+		scu = ipcdev;
+	if (!scu) {
 		mutex_unlock(&ipclock);
 		return -ENODEV;
 	}
-	scu = ipcdev;
 
-	for (i = 0; i < inlen; i++)
-		ipc_data_writel(scu, *in++, 4 * i);
+	memcpy(inbuf, in, inlen);
+	for (i = 0; i < inbuflen; i++)
+		ipc_data_writel(scu, inbuf[i], 4 * i);
 
-	cmdval = (inlen << 16) | (sub << 12) | cmd;
+	cmdval = (size << 16) | (sub << 12) | cmd;
 	ipc_command(scu, cmdval);
 	err = intel_scu_ipc_check_status(scu);
 
 	if (!err) {
-		for (i = 0; i < outlen; i++)
-			*out++ = ipc_data_readl(scu, 4 * i);
+		u32 outbuf[4] = {};
+
+		for (i = 0; i < outbuflen; i++)
+			outbuf[i] = ipc_data_readl(scu, 4 * i);
+
+		memcpy(out, outbuf, outlen);
 	}
 
 	mutex_unlock(&ipclock);
@@ -390,7 +514,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
 		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
 	return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
 
 /*
  * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
@@ -423,17 +547,20 @@ static void intel_scu_ipc_release(struct device *dev)
 }
 
 /**
- * intel_scu_ipc_register() - Register SCU IPC device
+ * __intel_scu_ipc_register() - Register SCU IPC device
  * @parent: Parent device
  * @pdata: Platform specific data for SCU IPC
+ * @owner: Module registering the SCU IPC device
  *
  * Call this function to register SCU IPC mechanism under @parent.
  * Returns pointer to the new SCU IPC device or ERR_PTR() in case of
- * failure.
+ * failure. The caller may use the returned instance if it needs to do
+ * SCU IPC calls itself.
  */
 struct intel_scu_ipc_dev *
-intel_scu_ipc_register(struct device *parent,
-		       const struct intel_scu_ipc_pdata *pdata)
+__intel_scu_ipc_register(struct device *parent,
+			 const struct intel_scu_ipc_pdata *pdata,
+			 struct module *owner)
 {
 	int err;
 	struct intel_scu_ipc_dev *scu;
@@ -452,6 +579,7 @@ intel_scu_ipc_register(struct device *parent,
 		goto err_unlock;
 	}
 
+	scu->owner = owner;
 	scu->dev.parent = parent;
 	scu->dev.class = &intel_scu_ipc_class;
 	scu->dev.release = intel_scu_ipc_release;
@@ -507,7 +635,7 @@ intel_scu_ipc_register(struct device *parent,
 
 	return ERR_PTR(err);
 }
-EXPORT_SYMBOL_GPL(intel_scu_ipc_register);
+EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
 
 static int __init intel_scu_ipc_init(void)
 {
-- 
2.25.0


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

* [PATCH v6 05/19] platform/x86: intel_mid_powerbtn: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (3 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 06/19] watchdog: intel-mid_wdt: " Mika Westerberg
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

This converts the power button driver to use the new SCU IPC API where
the SCU IPC instance is passed to the functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/platform/x86/intel_mid_powerbtn.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 6f436836fe50..1fdcdef1d89f 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -46,6 +46,7 @@ struct mid_pb_ddata {
 	unsigned short mirqlvl1_addr;
 	unsigned short pbstat_addr;
 	u8 pbstat_mask;
+	struct intel_scu_ipc_dev *scu;
 	int (*setup)(struct mid_pb_ddata *ddata);
 };
 
@@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
 	int ret;
 	u8 pbstat;
 
-	ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
+	ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
+					&pbstat);
 	if (ret)
 		return ret;
 
@@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
 
 static int mid_irq_ack(struct mid_pb_ddata *ddata)
 {
-	return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
+	return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
+					MSIC_PWRBTNM);
 }
 
 static int mrfld_setup(struct mid_pb_ddata *ddata)
 {
 	/* Unmask the PBIRQ and MPBIRQ on Tangier */
-	intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
-	intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
+	intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
+	intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
 
 	return 0;
 }
@@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
 			return error;
 	}
 
+	ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+	if (!ddata->scu)
+		return -EPROBE_DEFER;
+
 	error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
 					  IRQF_ONESHOT, DRIVER_NAME, ddata);
 	if (error) {
-- 
2.25.0


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

* [PATCH v6 06/19] watchdog: intel-mid_wdt: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (4 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 05/19] platform/x86: intel_mid_powerbtn: Convert to use " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 07/19] platform/x86: intel_scu_ipcutil: " Mika Westerberg
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

This converts the Intel MID watchdog driver over the new SCU IPC API
where the SCU IPC instance is passed to the functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/watchdog/intel-mid_wdt.c | 53 ++++++++++++++++++++++----------
 1 file changed, 37 insertions(+), 16 deletions(-)

diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 470213abfd3d..1ae03b64ef8b 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -33,14 +33,24 @@ enum {
 	SCU_WATCHDOG_KEEPALIVE,
 };
 
-static inline int wdt_command(int sub, u32 *in, int inlen)
+struct mid_wdt {
+	struct watchdog_device wd;
+	struct device *dev;
+	struct intel_scu_ipc_dev *scu;
+};
+
+static inline int
+wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
 {
-	return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+	struct intel_scu_ipc_dev *scu = mid->scu;
+
+	return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
+						   inlen, size, NULL, 0);
 }
 
 static int wdt_start(struct watchdog_device *wd)
 {
-	struct device *dev = watchdog_get_drvdata(wd);
+	struct mid_wdt *mid = watchdog_get_drvdata(wd);
 	int ret, in_size;
 	int timeout = wd->timeout;
 	struct ipc_wd_start {
@@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd)
 	} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
 
 	/*
-	 * SCU expects the input size for watchdog IPC to
-	 * be based on 4 bytes
+	 * SCU expects the input size for watchdog IPC to be 2 which is the
+	 * size of the structure in dwords. SCU IPC normally takes bytes
+	 * but this is a special case where we specify size to be different
+	 * than inlen.
 	 */
 	in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
 
-	ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
+	ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
+			  sizeof(ipc_wd_start), in_size);
 	if (ret)
-		dev_crit(dev, "error starting watchdog: %d\n", ret);
+		dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
 
 	return ret;
 }
 
 static int wdt_ping(struct watchdog_device *wd)
 {
-	struct device *dev = watchdog_get_drvdata(wd);
+	struct mid_wdt *mid = watchdog_get_drvdata(wd);
 	int ret;
 
-	ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
+	ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
 	if (ret)
-		dev_crit(dev, "Error executing keepalive: %d\n", ret);
+		dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
 
 	return ret;
 }
 
 static int wdt_stop(struct watchdog_device *wd)
 {
-	struct device *dev = watchdog_get_drvdata(wd);
+	struct mid_wdt *mid = watchdog_get_drvdata(wd);
 	int ret;
 
-	ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
+	ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
 	if (ret)
-		dev_crit(dev, "Error stopping watchdog: %d\n", ret);
+		dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
 
 	return ret;
 }
@@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct watchdog_device *wdt_dev;
 	struct intel_mid_wdt_pdata *pdata = dev->platform_data;
+	struct mid_wdt *mid;
 	int ret;
 
 	if (!pdata) {
@@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev)
 			return ret;
 	}
 
-	wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL);
-	if (!wdt_dev)
+	mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
+	if (!mid)
 		return -ENOMEM;
 
+	mid->dev = dev;
+	wdt_dev = &mid->wd;
+
 	wdt_dev->info = &mid_wdt_info;
 	wdt_dev->ops = &mid_wdt_ops;
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
@@ -135,7 +152,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	wdt_dev->parent = dev;
 
 	watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
-	watchdog_set_drvdata(wdt_dev, dev);
+	watchdog_set_drvdata(wdt_dev, mid);
 
 	ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
 			       IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
@@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	mid->scu = devm_intel_scu_ipc_dev_get(dev);
+	if (!mid->scu)
+		return -EPROBE_DEFER;
+
 	/*
 	 * The firmware followed by U-Boot leaves the watchdog running
 	 * with the default threshold which may vary. When we get here
-- 
2.25.0


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

* [PATCH v6 07/19] platform/x86: intel_scu_ipcutil: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (5 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 06/19] watchdog: intel-mid_wdt: " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 08/19] platform/x86: intel_scu_ipc: Add managed function to register SCU IPC Mika Westerberg
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Convert the IPC util to use the new SCU IPC API where the SCU IPC
instance is passed to the functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/platform/x86/intel_scu_ipcutil.c | 43 +++++++++++++++++++++---
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
index 8afe6fa06d7b..b7c10c15a3d6 100644
--- a/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/drivers/platform/x86/intel_scu_ipcutil.c
@@ -22,6 +22,9 @@
 
 static int major;
 
+struct intel_scu_ipc_dev *scu;
+static DEFINE_MUTEX(scu_lock);
+
 /* IOCTL commands */
 #define	INTE_SCU_IPC_REGISTER_READ	0
 #define INTE_SCU_IPC_REGISTER_WRITE	1
@@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data  *data)
 
 	switch (cmd) {
 	case INTE_SCU_IPC_REGISTER_READ:
-		return intel_scu_ipc_readv(data->addr, data->data, count);
+		return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);
 	case INTE_SCU_IPC_REGISTER_WRITE:
-		return intel_scu_ipc_writev(data->addr, data->data, count);
+		return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);
 	case INTE_SCU_IPC_REGISTER_UPDATE:
-		return intel_scu_ipc_update_register(data->addr[0],
-						    data->data[0], data->mask);
+		return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
+						data->mask);
 	default:
 		return -ENOTTY;
 	}
@@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
 	return 0;
 }
 
+static int scu_ipc_open(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+
+	/* Only single open at the time */
+	mutex_lock(&scu_lock);
+	if (scu) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	scu = intel_scu_ipc_dev_get();
+	if (!scu)
+		ret = -ENODEV;
+
+unlock:
+	mutex_unlock(&scu_lock);
+	return ret;
+}
+
+static int scu_ipc_release(struct inode *inode, struct file *file)
+{
+	mutex_lock(&scu_lock);
+	intel_scu_ipc_dev_put(scu);
+	scu = NULL;
+	mutex_unlock(&scu_lock);
+
+	return 0;
+}
+
 static const struct file_operations scu_ipc_fops = {
 	.unlocked_ioctl = scu_ipc_ioctl,
+	.open = scu_ipc_open,
+	.release = scu_ipc_release,
 };
 
 static int __init ipc_module_init(void)
-- 
2.25.0


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

* [PATCH v6 08/19] platform/x86: intel_scu_ipc: Add managed function to register SCU IPC
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (6 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 07/19] platform/x86: intel_scu_ipcutil: " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 09/19] platform/x86: intel_pmc_ipc: Start using " Mika Westerberg
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Drivers such as intel_pmc_ipc.c can be unloaded as well so in order to
support those in this driver add a new function that can be called to
unregister the SCU IPC when it is not needed anymore.

We also add a managed version of the intel_scu_ipc_register() that takes
care of calling intel_scu_ipc_unregister() automatically when the driver
is unbound.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_scu_ipc.h | 10 +++++
 drivers/platform/x86/intel_scu_ipc.c | 62 ++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)

diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 27041fe999e9..2463ff5cad5b 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -25,6 +25,16 @@ __intel_scu_ipc_register(struct device *parent,
 #define intel_scu_ipc_register(parent, pdata)  \
 	__intel_scu_ipc_register(parent, pdata, THIS_MODULE)
 
+void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
+
+struct intel_scu_ipc_dev *
+__devm_intel_scu_ipc_register(struct device *parent,
+			      const struct intel_scu_ipc_pdata *pdata,
+			      struct module *owner);
+
+#define devm_intel_scu_ipc_register(parent, pdata)  \
+	__devm_intel_scu_ipc_register(parent, pdata, THIS_MODULE)
+
 struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
 void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
 struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index a2f05b4dd2b9..896c65727a29 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -637,6 +637,68 @@ __intel_scu_ipc_register(struct device *parent,
 }
 EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
 
+/**
+ * intel_scu_ipc_unregister() - Unregister SCU IPC
+ * @scu: SCU IPC handle
+ *
+ * This unregisters the SCU IPC device and releases the acquired
+ * resources once the refcount goes to zero.
+ */
+void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
+{
+	mutex_lock(&ipclock);
+	if (!WARN_ON(!ipcdev)) {
+		ipcdev = NULL;
+		device_unregister(&scu->dev);
+	}
+	mutex_unlock(&ipclock);
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
+
+static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
+{
+	struct intel_scu_ipc_devres *dr = res;
+	struct intel_scu_ipc_dev *scu = dr->scu;
+
+	intel_scu_ipc_unregister(scu);
+}
+
+/**
+ * __devm_intel_scu_ipc_register() - Register managed SCU IPC device
+ * @parent: Parent device
+ * @pdata: Platform specific data
+ * @owner: Module registering the SCU IPC device
+ *
+ * Call this function to register managed SCU IPC mechanism under
+ * @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
+ * case of failure. The caller may use the returned instance if it needs
+ * to do SCU IPC calls itself.
+ */
+struct intel_scu_ipc_dev *
+__devm_intel_scu_ipc_register(struct device *parent,
+			      const struct intel_scu_ipc_pdata *pdata,
+			      struct module *owner)
+{
+	struct intel_scu_ipc_devres *dr;
+	struct intel_scu_ipc_dev *scu;
+
+	dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
+	if (!dr)
+		return NULL;
+
+	scu = __intel_scu_ipc_register(parent, pdata, owner);
+	if (IS_ERR(scu)) {
+		devres_free(dr);
+		return scu;
+	}
+
+	dr->scu = scu;
+	devres_add(parent, dr);
+
+	return scu;
+}
+EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
+
 static int __init intel_scu_ipc_init(void)
 {
 	return class_register(&intel_scu_ipc_class);
-- 
2.25.0


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

* [PATCH v6 09/19] platform/x86: intel_pmc_ipc: Start using SCU IPC
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (7 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 08/19] platform/x86: intel_scu_ipc: Add managed function to register SCU IPC Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic Mika Westerberg
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

SCU IPC is pretty much the same IPC implemented in the intel_pmc_ipc
driver so drop the duplicate implementation and call directly the SCU
IPC.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/platform/x86/Kconfig         |   1 +
 drivers/platform/x86/intel_pmc_ipc.c | 303 ++++-----------------------
 2 files changed, 40 insertions(+), 264 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index d2835400082b..6e99bd8fd806 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1322,6 +1322,7 @@ config INTEL_PMC_CORE
 config INTEL_PMC_IPC
 	tristate "Intel PMC IPC Driver"
 	depends on ACPI && PCI
+	select INTEL_SCU_IPC
 	---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 2433bf73f1ed..ea85b854c4c6 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -21,28 +21,10 @@
 #include <linux/platform_device.h>
 
 #include <asm/intel_pmc_ipc.h>
+#include <asm/intel_scu_ipc.h>
 
 #include <linux/platform_data/itco_wdt.h>
 
-/*
- * IPC registers
- * The IA write to IPC_CMD command register triggers an interrupt to the ARC,
- * 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			0x00
-#define		IPC_CMD_MSI		BIT(8)
-#define		IPC_CMD_SIZE		16
-#define		IPC_CMD_SUBCMD		12
-#define IPC_STATUS		0x04
-#define		IPC_STATUS_IRQ		BIT(2)
-#define		IPC_STATUS_ERR		BIT(1)
-#define		IPC_STATUS_BUSY		BIT(0)
-#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)		\
 ({						\
@@ -51,16 +33,6 @@
 	result;					\
 })
 
-/*
- * 16-byte buffer for sending data associated with IPC command.
- */
-#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
@@ -103,11 +75,6 @@
 
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
-	void __iomem *ipc_base;
-	bool irq_mode;
-	int irq;
-	int cmd;
-	struct completion cmd_complete;
 
 	/* The following PMC BARs share the same ACPI device with the IPC */
 	resource_size_t acpi_io_base;
@@ -132,53 +99,6 @@ static struct intel_pmc_ipc_dev {
 	struct platform_device *telemetry_dev;
 } 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",
-};
-
-/* 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 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);
@@ -274,127 +194,6 @@ static int update_no_reboot_bit(void *priv, bool set)
 				    PMC_CFG_NO_REBOOT_MASK, value);
 }
 
-static int intel_pmc_ipc_check_status(void)
-{
-	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 (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;
-	}
-
-	return ret;
-}
-
-/**
- * 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.
- */
-static int intel_pmc_ipc_simple_command(int cmd, int sub)
-{
-	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);
-
-	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.
- */
-static 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;
-
-	if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
-		return -EINVAL;
-
-	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);
-
-	return ret;
-}
-
 /**
  * intel_pmc_ipc_command() -  IPC command with input/output data
  * @cmd:	IPC command code.
@@ -411,54 +210,32 @@ static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
 int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
 			  u32 *out, u32 outlen)
 {
-	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+	return intel_scu_ipc_dev_command(NULL, cmd, sub, in, inlen, out, outlen);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
 
-static irqreturn_t ioc(int irq, void *dev_id)
-{
-	int status;
-
-	if (ipcdev.irq_mode) {
-		status = ipc_read_status();
-		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
-	}
-	complete(&ipcdev.cmd_complete);
-
-	return IRQ_HANDLED;
-}
-
 static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct intel_pmc_ipc_dev *pmc = &ipcdev;
+	struct intel_scu_ipc_pdata pdata = {};
+	struct intel_scu_ipc_dev *scu;
 	int ret;
 
 	/* Only one PMC is supported */
 	if (pmc->dev)
 		return -EBUSY;
 
-	pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
-
 	spin_lock_init(&ipcdev.gcr_lock);
 
 	ret = pcim_enable_device(pdev);
 	if (ret)
 		return ret;
 
-	ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
-	if (ret)
-		return ret;
-
-	init_completion(&pmc->cmd_complete);
-
-	pmc->ipc_base = pcim_iomap_table(pdev)[0];
+	pdata.mem = pdev->resource[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;
-	}
+	scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
+	if (IS_ERR(scu))
+		return PTR_ERR(scu);
 
 	pmc->dev = &pdev->dev;
 
@@ -485,6 +262,7 @@ static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
 					      struct device_attribute *attr,
 					      const char *buf, size_t count)
 {
+	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
 	int subcmd;
 	int cmd;
 	int ret;
@@ -495,7 +273,7 @@ static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
 		return -EINVAL;
 	}
 
-	ret = intel_pmc_ipc_simple_command(cmd, subcmd);
+	ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
 	if (ret) {
 		dev_err(dev, "command %d error with %d\n", cmd, ret);
 		return ret;
@@ -508,6 +286,7 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
 					     struct device_attribute *attr,
 					     const char *buf, size_t count)
 {
+	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
 	unsigned long val;
 	int subcmd;
 	int ret;
@@ -520,7 +299,7 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
 		subcmd = 1;
 	else
 		subcmd = 0;
-	ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
+	ret = intel_scu_ipc_dev_simple_command(scu, PMC_IPC_NORTHPEAK_CTRL, subcmd);
 	if (ret) {
 		dev_err(dev, "command north %d error with %d\n", subcmd, ret);
 		return ret;
@@ -714,9 +493,11 @@ static int ipc_create_pmc_devices(void)
 	return ret;
 }
 
-static int ipc_plat_get_res(struct platform_device *pdev)
+static int ipc_plat_get_res(struct platform_device *pdev,
+			    struct intel_scu_ipc_pdata *pdata)
 {
 	struct resource *res, *punit_res = punit_res_array;
+	resource_size_t start;
 	void __iomem *addr;
 	int size;
 
@@ -785,23 +566,30 @@ static int ipc_plat_get_res(struct platform_device *pdev)
 		dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
 	}
 
+	pdata->irq = platform_get_irq(pdev, 0);
+
 	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;
+	dev_info(&pdev->dev, "ipc res: %pR\n", res);
 
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(addr))
-		return PTR_ERR(addr);
+	pdata->mem.flags = res->flags;
+	pdata->mem.start = res->start;
+	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
 
-	ipcdev.ipc_base = addr;
+	start = res->start + PLAT_RESOURCE_GCR_OFFSET;
+	if (!devm_request_mem_region(&pdev->dev, start, PLAT_RESOURCE_GCR_SIZE,
+				     "pmc_ipc_plat"))
+		return -EBUSY;
 
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
+	addr = devm_ioremap(&pdev->dev, start, PLAT_RESOURCE_GCR_SIZE);
+	if (!addr)
+		return -ENOMEM;
+
+	ipcdev.gcr_mem_base = addr;
 
 	ipcdev.telem_res_inval = 0;
 	res = platform_get_resource(pdev, IORESOURCE_MEM,
@@ -854,51 +642,38 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
 
 static int ipc_plat_probe(struct platform_device *pdev)
 {
+	struct intel_scu_ipc_pdata pdata = {};
+	struct intel_scu_ipc_dev *scu;
 	int ret;
 
 	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)
-		return -EINVAL;
-
-	ret = ipc_plat_get_res(pdev);
+	ret = ipc_plat_get_res(pdev, &pdata);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to request resource\n");
 		return ret;
 	}
 
+	scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
+	if (IS_ERR(scu))
+		return PTR_ERR(scu);
+
+	platform_set_drvdata(pdev, scu);
+
 	ret = ipc_create_pmc_devices();
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
 		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");
-		ret = -EBUSY;
-		goto err_irq;
-	}
-
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-
-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)
 {
-	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);
-- 
2.25.0


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

* [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (8 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 09/19] platform/x86: intel_pmc_ipc: Start using " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-24 15:30   ` Lee Jones
  2020-02-17 13:14 ` [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API Mika Westerberg
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Both PMIC drivers (intel_soc_pmic_mrfld and intel_soc_pmic_bxtwc) will
be using this field going forward to access the SCU IPC instance.

While there add kernel-doc for the intel_soc_pmic structure.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 include/linux/mfd/intel_soc_pmic.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
index bfecd6bd4990..6a88e34cb955 100644
--- a/include/linux/mfd/intel_soc_pmic.h
+++ b/include/linux/mfd/intel_soc_pmic.h
@@ -13,6 +13,20 @@
 
 #include <linux/regmap.h>
 
+/**
+ * struct intel_soc_pmic - Intel SoC PMIC data
+ * @irq: Master interrupt number of the parent PMIC device
+ * @regmap: Pointer to the parent PMIC device regmap structure
+ * @irq_chip_data: IRQ chip data for the PMIC itself
+ * @irq_chip_data_pwrbtn: Chained IRQ chip data for the Power Button
+ * @irq_chip_data_tmu: Chained IRQ chip data for the Time Management Unit
+ * @irq_chip_data_bcu: Chained IRQ chip data for the Burst Control Unit
+ * @irq_chip_data_adc: Chained IRQ chip data for the General Purpose ADC
+ * @irq_chip_data_chgr: Chained IRQ chip data for the External Charger
+ * @irq_chip_data_crit: Chained IRQ chip data for the Critical Event Handler
+ * @dev: Pointer to the parent PMIC device
+ * @scu: Pointer to the SCU IPC device data structure
+ */
 struct intel_soc_pmic {
 	int irq;
 	struct regmap *regmap;
@@ -24,6 +38,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_scu_ipc_dev *scu;
 };
 
 int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
-- 
2.25.0


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

* [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (9 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-25  9:24   ` Lee Jones
  2020-02-17 13:14 ` [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: " Mika Westerberg
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Convert the Intel Broxton Whiskey Cover PMIC driver to use the new SCU
IPC API. This allows us to get rid of the PMC IPC implementation which
is now covered in SCU IPC driver. We drop the error log if the IPC
command fails because intel_scu_ipc_dev_command() does that already.

Also move PMIC specific IPC message constants to the PMIC driver from
the intel_pmc_ipc.h header.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_pmc_ipc.h |  3 ---
 drivers/mfd/intel_soc_pmic_bxtwc.c   | 34 ++++++++++++++--------------
 2 files changed, 17 insertions(+), 20 deletions(-)

diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index e6da1ce26256..b438a488f613 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -3,9 +3,6 @@
 #define  _ASM_X86_INTEL_PMC_IPC_H_
 
 /* Commands */
-#define PMC_IPC_PMIC_ACCESS		0xFF
-#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
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 739cfb5b69fe..eba89780dbe7 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -15,7 +15,7 @@
 #include <linux/mfd/intel_soc_pmic_bxtwc.h>
 #include <linux/module.h>
 
-#include <asm/intel_pmc_ipc.h>
+#include <asm/intel_scu_ipc.h>
 
 /* PMIC device registers */
 #define REG_ADDR_MASK		0xFF00
@@ -58,6 +58,10 @@
 /* Whiskey Cove PMIC share same ACPI ID between different platforms */
 #define BROXTON_PMIC_WC_HRV	4
 
+#define PMC_PMIC_ACCESS		0xFF
+#define PMC_PMIC_READ		0x0
+#define PMC_PMIC_WRITE		0x1
+
 enum bxtwc_irqs {
 	BXTWC_PWRBTN_LVL1_IRQ = 0,
 	BXTWC_TMU_LVL1_IRQ,
@@ -288,13 +292,12 @@ 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);
-	if (ret) {
-		dev_err(pmic->dev, "Failed to read from PMIC\n");
+	ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
+					PMC_PMIC_READ, ipc_in, sizeof(ipc_in),
+					ipc_out, sizeof(ipc_out));
+	if (ret)
 		return ret;
-	}
+
 	*val = ipc_out[0];
 
 	return 0;
@@ -303,7 +306,6 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
 				       unsigned int val)
 {
-	int ret;
 	int i2c_addr;
 	u8 ipc_in[3];
 	struct intel_soc_pmic *pmic = context;
@@ -321,15 +323,9 @@ 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);
-	if (ret) {
-		dev_err(pmic->dev, "Failed to write to PMIC\n");
-		return ret;
-	}
-
-	return 0;
+	return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
+					 PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in),
+					 NULL, 0);
 }
 
 /* sysfs interfaces to r/w PMIC registers, required by initial script */
@@ -457,6 +453,10 @@ static int bxtwc_probe(struct platform_device *pdev)
 	dev_set_drvdata(&pdev->dev, pmic);
 	pmic->dev = &pdev->dev;
 
+	pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+	if (!pmic->scu)
+		return -EPROBE_DEFER;
+
 	pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
 					&bxtwc_regmap_config);
 	if (IS_ERR(pmic->regmap)) {
-- 
2.25.0


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

* [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (10 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-26  6:45   ` Lee Jones
  2020-02-17 13:14 ` [PATCH v6 13/19] platform/x86: intel_telemetry: " Mika Westerberg
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

This converts the Intel Merrifield PMIC driver over the new SCU IPC API
where the SCU IPC instance is passed to the functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/mfd/intel_soc_pmic_mrfld.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/mfd/intel_soc_pmic_mrfld.c b/drivers/mfd/intel_soc_pmic_mrfld.c
index 26a1551c5faf..bd94c989d232 100644
--- a/drivers/mfd/intel_soc_pmic_mrfld.c
+++ b/drivers/mfd/intel_soc_pmic_mrfld.c
@@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = {
 static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
 				    unsigned int *val)
 {
+	struct intel_soc_pmic *pmic = context;
 	u8 ipc_out;
 	int ret;
 
-	ret = intel_scu_ipc_ioread8(reg, &ipc_out);
+	ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out);
 	if (ret)
 		return ret;
 
@@ -88,10 +89,11 @@ static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
 static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
 				     unsigned int val)
 {
+	struct intel_soc_pmic *pmic = context;
 	u8 ipc_in = val;
 	int ret;
 
-	ret = intel_scu_ipc_iowrite8(reg, ipc_in);
+	ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
 	if (ret)
 		return ret;
 
@@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev)
 	if (!pmic)
 		return -ENOMEM;
 
+	pmic->scu = devm_intel_scu_ipc_dev_get(dev);
+	if (!pmic->scu)
+		return -ENOMEM;
+
 	platform_set_drvdata(pdev, pmic);
 	pmic->dev = &pdev->dev;
 
-- 
2.25.0


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

* [PATCH v6 13/19] platform/x86: intel_telemetry: Convert to use new SCU IPC API
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (11 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 14/19] platform/x86: intel_pmc_ipc: Drop intel_pmc_ipc_command() Mika Westerberg
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Convert the Intel Apollo Lake telemetry driver to use the new SCU IPC
API. This allows us to get rid of the duplicate PMC IPC implementation
which is now covered in SCU IPC driver.

Also move telemetry specific IPC message constant to the telemetry
driver where it belongs.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_pmc_ipc.h          |  1 -
 arch/x86/include/asm/intel_telemetry.h        |  3 +
 drivers/platform/x86/intel_telemetry_pltdrv.c | 95 +++++++++----------
 3 files changed, 49 insertions(+), 50 deletions(-)

diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index b438a488f613..ddc964b9c78c 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -8,7 +8,6 @@
 #define PMC_IPC_PHY_CONFIG		0xEE
 #define PMC_IPC_NORTHPEAK_CTRL		0xED
 #define PMC_IPC_PM_DEBUG		0xEC
-#define PMC_IPC_PMC_TELEMTRY		0xEB
 #define PMC_IPC_PMC_FW_MSG_CTRL		0xEA
 
 /* IPC return code */
diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h
index 2f77e31a1283..274aaf0dae48 100644
--- a/arch/x86/include/asm/intel_telemetry.h
+++ b/arch/x86/include/asm/intel_telemetry.h
@@ -10,6 +10,8 @@
 #define TELEM_MAX_EVENTS_SRAM		28
 #define TELEM_MAX_OS_ALLOCATED_EVENTS	20
 
+#include <asm/intel_scu_ipc.h>
+
 enum telemetry_unit {
 	TELEM_PSS = 0,
 	TELEM_IOSS,
@@ -51,6 +53,7 @@ struct telemetry_plt_config {
 	struct telemetry_unit_config ioss_config;
 	struct mutex telem_trace_lock;
 	struct mutex telem_lock;
+	struct intel_scu_ipc_dev *scu;
 	bool telem_in_use;
 };
 
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index c4c742bb23cf..efcf214d25b1 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -15,7 +15,6 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/intel_pmc_ipc.h>
 #include <asm/intel_punit_ipc.h>
 #include <asm/intel_telemetry.h>
 
@@ -35,6 +34,7 @@
 #define TELEM_SSRAM_STARTTIME_OFFSET	8
 #define TELEM_SSRAM_EVTLOG_OFFSET	16
 
+#define IOSS_TELEM			0xeb
 #define IOSS_TELEM_EVENT_READ		0x0
 #define IOSS_TELEM_EVENT_WRITE		0x1
 #define IOSS_TELEM_INFO_READ		0x2
@@ -42,9 +42,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
@@ -253,17 +250,14 @@ 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;
 
 	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);
-
-	return ret;
+	return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+					 IOSS_TELEM_EVENT_WRITE, &write_buf,
+					 IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
 }
 
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -281,6 +275,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 					 enum telemetry_action action)
 {
+	struct intel_scu_ipc_dev *scu = telm_conf->scu;
 	u8 num_ioss_evts, ioss_period;
 	int ret, index, idx;
 	u32 *ioss_evtmap;
@@ -291,9 +286,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
 	ioss_evtmap = evtconfig.evtmap;
 
 	/* Get telemetry EVENT CTL */
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+	ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
 				    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-				    &telem_ctrl, IOSS_TELEM_READ_WORD);
+				    &telem_ctrl, sizeof(telem_ctrl));
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Read Failed\n");
 		return ret;
@@ -302,11 +297,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);
+	ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+				    IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
+				    sizeof(telem_ctrl), NULL, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 		return ret;
@@ -318,10 +311,9 @@ 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,
+		ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
 					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
+					    &telem_ctrl, sizeof(telem_ctrl),
 					    NULL, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -347,10 +339,9 @@ 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,
+		ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
 					    IOSS_TELEM_EVENT_CTL_WRITE,
-					    (u8 *)&telem_ctrl,
-					    IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
+					    &telem_ctrl, sizeof(telem_ctrl),
 					    NULL, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -399,10 +390,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,
+	ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
 				    IOSS_TELEM_EVENT_CTL_WRITE,
-				    (u8 *)&telem_ctrl,
-				    IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
+				    &telem_ctrl, sizeof(telem_ctrl), NULL, 0);
 	if (ret) {
 		pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 		return ret;
@@ -589,8 +579,9 @@ static int telemetry_setup(struct platform_device *pdev)
 	u32 read_buf, events, event_regs;
 	int ret;
 
-	ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
-				    NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
+	ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+					IOSS_TELEM_INFO_READ, NULL, 0,
+					&read_buf, sizeof(read_buf));
 	if (ret) {
 		dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
 		return ret;
@@ -684,6 +675,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
 	mutex_lock(&(telm_conf->telem_lock));
 	if (ioss_period) {
+		struct intel_scu_ipc_dev *scu = telm_conf->scu;
+
 		if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
 			pr_err("IOSS Sampling Period Out of Range\n");
 			ret = -EINVAL;
@@ -691,9 +684,9 @@ 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,
+		ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
 					    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-					    &telem_ctrl, IOSS_TELEM_READ_WORD);
+					    &telem_ctrl, sizeof(telem_ctrl));
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Read Failed\n");
 			goto out;
@@ -702,11 +695,10 @@ 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);
+		ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+						IOSS_TELEM_EVENT_CTL_WRITE,
+						&telem_ctrl, sizeof(telem_ctrl),
+						NULL, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
 			goto out;
@@ -718,11 +710,10 @@ 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);
+		ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+						IOSS_TELEM_EVENT_CTL_WRITE,
+						&telem_ctrl, sizeof(telem_ctrl),
+						NULL, 0);
 		if (ret) {
 			pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
 			goto out;
@@ -1017,9 +1008,9 @@ 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);
+		ret = intel_scu_ipc_dev_command(telm_conf->scu,
+				IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
+				NULL, 0, &temp, sizeof(temp));
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1071,9 +1062,9 @@ 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);
+		ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+						IOSS_TELEM_TRACE_CTL_READ,
+						NULL, 0, &temp, sizeof(temp));
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Read Failed\n");
 			goto out;
@@ -1082,9 +1073,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);
+		ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+						IOSS_TELEM_TRACE_CTL_WRITE,
+						&temp, sizeof(temp), NULL, 0);
 		if (ret) {
 			pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
 			goto out;
@@ -1139,6 +1130,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 
 	telm_conf->ioss_config.regmap = mem;
 
+	telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+	if (!telm_conf->scu) {
+		ret = -EPROBE_DEFER;
+		goto out;
+	}
+
 	mutex_init(&telm_conf->telem_lock);
 	mutex_init(&telm_conf->telem_trace_lock);
 
-- 
2.25.0


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

* [PATCH v6 14/19] platform/x86: intel_pmc_ipc: Drop intel_pmc_ipc_command()
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (12 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 13/19] platform/x86: intel_telemetry: " Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 15/19] x86/platform/intel-mid: Add empty stubs for intel_scu_devices_[create|destroy]() Mika Westerberg
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Now that all callers have been converted over to the SCU IPC API we can
drop intel_pmc_ipc_command().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_pmc_ipc.h |  8 --------
 drivers/platform/x86/intel_pmc_ipc.c | 20 --------------------
 2 files changed, 28 deletions(-)

diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index ddc964b9c78c..22848df5faaf 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -27,19 +27,11 @@
 
 #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
 
-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_read64(u32 offset, u64 *data);
 
 #else
 
-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/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index ea85b854c4c6..3ec7a0d1e9b0 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -194,26 +194,6 @@ static int update_no_reboot_bit(void *priv, bool set)
 				    PMC_CFG_NO_REBOOT_MASK, value);
 }
 
-/**
- * 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)
-{
-	return intel_scu_ipc_dev_command(NULL, cmd, sub, in, inlen, out, outlen);
-}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
-
 static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct intel_pmc_ipc_dev *pmc = &ipcdev;
-- 
2.25.0


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

* [PATCH v6 15/19] x86/platform/intel-mid: Add empty stubs for intel_scu_devices_[create|destroy]()
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (13 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 14/19] platform/x86: intel_pmc_ipc: Drop intel_pmc_ipc_command() Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 16/19] platform/x86: intel_pmc_ipc: Move PCI IDs to intel_scu_pcidrv.c Mika Westerberg
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

This allows to call the functions even when CONFIG_X86_INTEL_MID is not
enabled.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel-mid.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/intel-mid.h b/arch/x86/include/asm/intel-mid.h
index 8e5af119dc2d..de58391bdee0 100644
--- a/arch/x86/include/asm/intel-mid.h
+++ b/arch/x86/include/asm/intel-mid.h
@@ -88,11 +88,17 @@ static inline bool intel_mid_has_msic(void)
 	return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL);
 }
 
+extern void intel_scu_devices_create(void);
+extern void intel_scu_devices_destroy(void);
+
 #else /* !CONFIG_X86_INTEL_MID */
 
 #define intel_mid_identify_cpu()	0
 #define intel_mid_has_msic()		0
 
+static inline void intel_scu_devices_create(void) { }
+static inline void intel_scu_devices_destroy(void) { }
+
 #endif /* !CONFIG_X86_INTEL_MID */
 
 enum intel_mid_timer_options {
@@ -115,9 +121,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
 #define SFI_MTMR_MAX_NUM		8
 #define SFI_MRTC_MAX			8
 
-extern void intel_scu_devices_create(void);
-extern void intel_scu_devices_destroy(void);
-
 /* VRTC timer */
 #define MRST_VRTC_MAP_SZ		1024
 /* #define MRST_VRTC_PGOFFSET		0xc00 */
-- 
2.25.0


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

* [PATCH v6 16/19] platform/x86: intel_pmc_ipc: Move PCI IDs to intel_scu_pcidrv.c
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (14 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 15/19] x86/platform/intel-mid: Add empty stubs for intel_scu_devices_[create|destroy]() Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 17/19] platform/x86: intel_telemetry: Add telemetry_get_pltdata() Mika Westerberg
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

The PCI probe driver in intel_pmc_ipc.c is a duplicate of what we
already have in intel_scu_pcidrv.c with the exception that the later also
creates SCU specific devices. Move the PCI IDs from the intel_pmc_ipc.c
to intel_scu.c and use driver_data to detect whether SCU devices need to
be created or not.

Also update Kconfig entry to mention all platforms supported by the
Intel SCU PCI driver and change dependency from X86_INTEL_MID to PCI
which is more generic.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/platform/x86/Kconfig            | 13 ++++--
 drivers/platform/x86/intel_pmc_ipc.c    | 61 +------------------------
 drivers/platform/x86/intel_scu_pcidrv.c | 21 +++++++--
 3 files changed, 27 insertions(+), 68 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 6e99bd8fd806..65eb53fc9206 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1321,7 +1321,7 @@ config INTEL_PMC_CORE
 
 config INTEL_PMC_IPC
 	tristate "Intel PMC IPC Driver"
-	depends on ACPI && PCI
+	depends on ACPI
 	select INTEL_SCU_IPC
 	---help---
 	This driver provides support for PMC control on some Intel platforms.
@@ -1343,13 +1343,18 @@ config INTEL_SCU
 
 config INTEL_SCU_PCI
 	bool "Intel SCU PCI driver"
-	depends on X86_INTEL_MID
+	depends on PCI
 	select INTEL_SCU
 	help
 	  This driver is used to bridge the communications between kernel
 	  and SCU on some embedded Intel x86 platforms. It also creates
-	  devices that are connected to the SoC through the SCU. This is
-	  not needed for PC-type machines.
+	  devices that are connected to the SoC through the SCU.
+	  Platforms supported:
+	    Medfield
+	    Clovertrail
+	    Merrifield
+	    Broxton
+	    Apollo Lake
 
 config INTEL_SCU_IPC_UTIL
 	tristate "Intel SCU IPC utility driver"
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 3ec7a0d1e9b0..c006609ef74b 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -17,7 +17,6 @@
 #include <linux/interrupt.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #include <linux/platform_device.h>
 
 #include <asm/intel_pmc_ipc.h>
@@ -194,50 +193,6 @@ static int update_no_reboot_bit(void *priv, bool set)
 				    PMC_CFG_NO_REBOOT_MASK, value);
 }
 
-static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-	struct intel_pmc_ipc_dev *pmc = &ipcdev;
-	struct intel_scu_ipc_pdata pdata = {};
-	struct intel_scu_ipc_dev *scu;
-	int ret;
-
-	/* Only one PMC is supported */
-	if (pmc->dev)
-		return -EBUSY;
-
-	spin_lock_init(&ipcdev.gcr_lock);
-
-	ret = pcim_enable_device(pdev);
-	if (ret)
-		return ret;
-
-	pdata.mem = pdev->resource[0];
-
-	scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
-	if (IS_ERR(scu))
-		return PTR_ERR(scu);
-
-	pmc->dev = &pdev->dev;
-
-	pci_set_drvdata(pdev, pmc);
-
-	return 0;
-}
-
-static const struct pci_device_id ipc_pci_ids[] = {
-	{PCI_VDEVICE(INTEL, 0x0a94), 0},
-	{PCI_VDEVICE(INTEL, 0x1a94), 0},
-	{PCI_VDEVICE(INTEL, 0x5a94), 0},
-	{ 0,}
-};
-MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
-
-static struct pci_driver ipc_pci_driver = {
-	.name = "intel_pmc_ipc",
-	.id_table = ipc_pci_ids,
-	.probe = ipc_pci_probe,
-};
-
 static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
 					      struct device_attribute *attr,
 					      const char *buf, size_t count)
@@ -673,25 +628,11 @@ static struct platform_driver ipc_plat_driver = {
 
 static int __init intel_pmc_ipc_init(void)
 {
-	int ret;
-
-	ret = platform_driver_register(&ipc_plat_driver);
-	if (ret) {
-		pr_err("Failed to register PMC ipc platform driver\n");
-		return ret;
-	}
-	ret = pci_register_driver(&ipc_pci_driver);
-	if (ret) {
-		pr_err("Failed to register PMC ipc pci driver\n");
-		platform_driver_unregister(&ipc_plat_driver);
-		return ret;
-	}
-	return ret;
+	return platform_driver_register(&ipc_plat_driver);
 }
 
 static void __exit intel_pmc_ipc_exit(void)
 {
-	pci_unregister_driver(&ipc_pci_driver);
 	platform_driver_unregister(&ipc_plat_driver);
 }
 
diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c
index 52495ce14b9c..d2d0efcf6379 100644
--- a/drivers/platform/x86/intel_scu_pcidrv.c
+++ b/drivers/platform/x86/intel_scu_pcidrv.c
@@ -17,6 +17,7 @@
 static int intel_scu_pci_probe(struct pci_dev *pdev,
 			       const struct pci_device_id *id)
 {
+	void (*setup_fn)(void) = (void (*)(void))id->driver_data;
 	struct intel_scu_ipc_pdata pdata = {};
 	struct intel_scu_ipc_dev *scu;
 	int ret;
@@ -32,14 +33,26 @@ static int intel_scu_pci_probe(struct pci_dev *pdev,
 	if (IS_ERR(scu))
 		return PTR_ERR(scu);
 
-	intel_scu_devices_create();
+	if (setup_fn)
+		setup_fn();
 	return 0;
 }
 
+static void intel_mid_scu_setup(void)
+{
+	intel_scu_devices_create();
+}
+
 static const struct pci_device_id pci_ids[] = {
-	{ PCI_VDEVICE(INTEL, 0x080e) },
-	{ PCI_VDEVICE(INTEL, 0x08ea) },
-	{ PCI_VDEVICE(INTEL, 0x11a0) },
+	{ PCI_VDEVICE(INTEL, 0x080e),
+	  .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+	{ PCI_VDEVICE(INTEL, 0x08ea),
+	  .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+	{ PCI_VDEVICE(INTEL, 0x0a94) },
+	{ PCI_VDEVICE(INTEL, 0x11a0),
+	  .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+	{ PCI_VDEVICE(INTEL, 0x1a94) },
+	{ PCI_VDEVICE(INTEL, 0x5a94) },
 	{}
 };
 
-- 
2.25.0


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

* [PATCH v6 17/19] platform/x86: intel_telemetry: Add telemetry_get_pltdata()
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (15 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 16/19] platform/x86: intel_pmc_ipc: Move PCI IDs to intel_scu_pcidrv.c Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD Mika Westerberg
  2020-02-17 13:14 ` [PATCH v6 19/19] MAINTAINERS: Update entry for Intel Broxton PMC driver Mika Westerberg
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

Add new function that allows telemetry modules to get pointer to the
platform specific configuration. This is needed to allow the telemetry
debugfs module to fetch PMC IPC instance in the subsequent patch.

This also allows us to replace telemetry_pltconfig_valid() with
telemetry_get_pltdata() as well.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/x86/include/asm/intel_telemetry.h         |  2 +-
 drivers/platform/x86/intel_telemetry_core.c    | 17 ++++++-----------
 drivers/platform/x86/intel_telemetry_debugfs.c |  3 +--
 3 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h
index 274aaf0dae48..2c0e7d7a10e9 100644
--- a/arch/x86/include/asm/intel_telemetry.h
+++ b/arch/x86/include/asm/intel_telemetry.h
@@ -95,7 +95,7 @@ int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
 
 int telemetry_clear_pltdata(void);
 
-int telemetry_pltconfig_valid(void);
+struct telemetry_plt_config *telemetry_get_pltdata(void);
 
 int telemetry_get_evtname(enum telemetry_unit telem_unit,
 			  const char **name, int len);
diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c
index d4040bb222b4..fdf55b5d6948 100644
--- a/drivers/platform/x86/intel_telemetry_core.c
+++ b/drivers/platform/x86/intel_telemetry_core.c
@@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
 EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
 
 /**
- * telemetry_pltconfig_valid() - Checkif platform config is valid
+ * telemetry_get_pltdata() - Return telemetry platform config
  *
- * Usage by other than telemetry module is invalid
- *
- * Return: 0 success, < 0 for failure
+ * May be used by other telemetry modules to get platform specific
+ * configuration.
  */
-int telemetry_pltconfig_valid(void)
+struct telemetry_plt_config *telemetry_get_pltdata(void)
 {
-	if (telm_core_conf.plt_config)
-		return 0;
-
-	else
-		return -EINVAL;
+	return telm_core_conf.plt_config;
 }
-EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
+EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
 
 static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
 					   const char **name, int len)
diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c
index 8e3fb55ac1ae..85a456aa0ab9 100644
--- a/drivers/platform/x86/intel_telemetry_debugfs.c
+++ b/drivers/platform/x86/intel_telemetry_debugfs.c
@@ -911,8 +911,7 @@ static int __init telemetry_debugfs_init(void)
 
 	debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
 
-	err = telemetry_pltconfig_valid();
-	if (err < 0) {
+	if (!telemetry_get_pltdata()) {
 		pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
 		return -ENODEV;
 	}
-- 
2.25.0


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

* [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (16 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 17/19] platform/x86: intel_telemetry: Add telemetry_get_pltdata() Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  2020-02-26  8:47   ` Lee Jones
  2020-02-17 13:14 ` [PATCH v6 19/19] MAINTAINERS: Update entry for Intel Broxton PMC driver Mika Westerberg
  18 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

This driver only creates a bunch of platform devices sharing resources
belonging to the PMC device. This is pretty much what MFD subsystem is
for so move the driver there, renaming it to intel_pmc_bxt.c which
should be more clear what it is.

MFD subsystem provides nice helper APIs for subdevice creation so
convert the driver to use those. Unfortunately the ACPI device includes
separate resources for most of the subdevices so we cannot simply call
mfd_add_devices() to create all of them but instead we need to call it
separately for each device.

The new MFD driver continues to expose two sysfs attributes that allow
userspace to send IPC commands to the PMC/SCU to avoid breaking any
existing applications that may use these. Generally this is bad idea so
document this in the ABI documentation.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
 arch/x86/include/asm/intel_pmc_ipc.h          |  47 --
 arch/x86/include/asm/intel_telemetry.h        |   1 +
 drivers/mfd/Kconfig                           |  16 +-
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++++++
 drivers/platform/x86/Kconfig                  |  16 +-
 drivers/platform/x86/Makefile                 |   1 -
 drivers/platform/x86/intel_pmc_ipc.c          | 645 ------------------
 .../platform/x86/intel_telemetry_debugfs.c    |  12 +-
 drivers/platform/x86/intel_telemetry_pltdrv.c |   2 +
 drivers/usb/typec/tcpm/Kconfig                |   2 +-
 include/linux/mfd/intel_pmc_bxt.h             |  21 +
 13 files changed, 565 insertions(+), 710 deletions(-)
 create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
 delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
 create mode 100644 drivers/mfd/intel_pmc_bxt.c
 delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
 create mode 100644 include/linux/mfd/intel_pmc_bxt.h

diff --git a/Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt b/Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
new file mode 100644
index 000000000000..b298be32d426
--- /dev/null
+++ b/Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
@@ -0,0 +1,22 @@
+These files allow sending arbitrary IPC commands to the PMC/SCU which
+may be dangerous. These will be removed eventually and should not be
+used in any new applications.
+
+What:		/sys/bus/platform/devices/INT34D2:00/simplecmd
+Date:		Jun 2015
+KernelVersion:	4.1
+Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:	This interface allows userspace to send an arbitrary
+		IPC command to the PMC/SCU.
+
+		Format: %d %d where first number is command and
+		second number is subcommand.
+
+What:		/sys/bus/platform/devices/INT34D2:00/northpeak
+Date:		Jun 2015
+KernelVersion:	4.1
+Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:	This interface allows userspace to send an arbitrary
+		command to the Northpeak through the PMC/SCU.
+
+		Format: %u.
diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
deleted file mode 100644
index 22848df5faaf..000000000000
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_X86_INTEL_PMC_IPC_H_
-#define  _ASM_X86_INTEL_PMC_IPC_H_
-
-/* Commands */
-#define PMC_IPC_USB_PWR_CTRL		0xF0
-#define PMC_IPC_PMIC_BLACKLIST_SEL	0xEF
-#define PMC_IPC_PHY_CONFIG		0xEE
-#define PMC_IPC_NORTHPEAK_CTRL		0xED
-#define PMC_IPC_PM_DEBUG		0xEC
-#define PMC_IPC_PMC_FW_MSG_CTRL		0xEA
-
-/* IPC return code */
-#define IPC_ERR_NONE			0
-#define IPC_ERR_CMD_NOT_SUPPORTED	1
-#define IPC_ERR_CMD_NOT_SERVICED	2
-#define IPC_ERR_UNABLE_TO_SERVICE	3
-#define IPC_ERR_CMD_INVALID		4
-#define IPC_ERR_CMD_FAILED		5
-#define IPC_ERR_EMSECURITY		6
-#define IPC_ERR_UNSIGNEDKERNEL		7
-
-/* GCR reg offsets from gcr base*/
-#define PMC_GCR_PMC_CFG_REG		0x08
-#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
-#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
-
-#if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
-
-int intel_pmc_s0ix_counter_read(u64 *data);
-int intel_pmc_gcr_read64(u32 offset, u64 *data);
-
-#else
-
-static inline int intel_pmc_s0ix_counter_read(u64 *data)
-{
-	return -EINVAL;
-}
-
-static inline int intel_pmc_gcr_read64(u32 offset, u64 *data)
-{
-	return -EINVAL;
-}
-
-#endif /*CONFIG_INTEL_PMC_IPC*/
-
-#endif
diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h
index 2c0e7d7a10e9..8046e70dfd7c 100644
--- a/arch/x86/include/asm/intel_telemetry.h
+++ b/arch/x86/include/asm/intel_telemetry.h
@@ -53,6 +53,7 @@ struct telemetry_plt_config {
 	struct telemetry_unit_config ioss_config;
 	struct mutex telem_trace_lock;
 	struct mutex telem_lock;
+	struct intel_pmc_dev *pmc;
 	struct intel_scu_ipc_dev *scu;
 	bool telem_in_use;
 };
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 20b294ef2873..d41a965d819b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -551,7 +551,7 @@ config INTEL_SOC_PMIC
 
 config INTEL_SOC_PMIC_BXTWC
 	tristate "Support for Intel Broxton Whiskey Cove PMIC"
-	depends on INTEL_PMC_IPC
+	depends on MFD_INTEL_PMC_BXT
 	select MFD_CORE
 	select REGMAP_IRQ
 	help
@@ -632,6 +632,20 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config MFD_INTEL_PMC_BXT
+	tristate "Intel PMC Driver for Broxton"
+	depends on X86
+	depends on X86_PLATFORM_DEVICES
+	depends on ACPI
+	select INTEL_SCU_IPC
+	select MFD_CORE
+	help
+	  This driver provides support for the PMC (Power Management
+	  Controller) on Intel Broxton and Apollo Lake. The PMC is a
+	  multi-function device that exposes IPC, General Control
+	  Register and P-unit access. In addition this creates devices
+	  for iTCO watchdog and telemetry that are part of the PMC.
+
 config MFD_IPAQ_MICRO
 	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
 	depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b83f172545e1..444264d42a20 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -212,6 +212,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
 obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PMC_BXT)	+= intel_pmc_bxt.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c
new file mode 100644
index 000000000000..7f743ae205de
--- /dev/null
+++ b/drivers/mfd/intel_pmc_bxt.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Intel Broxton PMC
+ *
+ * (C) Copyright 2014 - 2020 Intel Corporation
+ *
+ * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
+ * Sreedhara DS <sreedhara.ds@intel.com>
+ *
+ * The PMC running on the ARC processor communicates with another entity
+ * running in the IA core through an IPC mechanism which in turn sends
+ * messages between the IA and the PMC.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_pmc_bxt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/itco_wdt.h>
+
+#include <asm/intel_scu_ipc.h>
+
+/* Residency with clock rate at 19.2MHz to usecs */
+#define S0IX_RESIDENCY_IN_USECS(d, s)		\
+({						\
+	u64 result = 10ull * ((d) + (s));	\
+	do_div(result, 192);			\
+	result;					\
+})
+
+/* Resources exported from IFWI */
+#define PLAT_RESOURCE_IPC_INDEX		0
+#define PLAT_RESOURCE_IPC_SIZE		0x1000
+#define PLAT_RESOURCE_GCR_OFFSET	0x1000
+#define PLAT_RESOURCE_GCR_SIZE		0x1000
+#define PLAT_RESOURCE_BIOS_DATA_INDEX	1
+#define PLAT_RESOURCE_BIOS_IFACE_INDEX	2
+#define PLAT_RESOURCE_TELEM_SSRAM_INDEX	3
+#define PLAT_RESOURCE_ISP_DATA_INDEX	4
+#define PLAT_RESOURCE_ISP_IFACE_INDEX	5
+#define PLAT_RESOURCE_GTD_DATA_INDEX	6
+#define PLAT_RESOURCE_GTD_IFACE_INDEX	7
+#define PLAT_RESOURCE_ACPI_IO_INDEX	0
+
+/*
+ * BIOS does not create an ACPI device for each PMC function, but
+ * exports multiple resources from one ACPI device (IPC) for multiple
+ * functions. This driver is responsible for creating a child device and
+ * to export resources for those functions.
+ */
+#define TCO_DEVICE_NAME			"iTCO_wdt"
+#define SMI_EN_OFFSET			0x40
+#define SMI_EN_SIZE			4
+#define TCO_BASE_OFFSET			0x60
+#define TCO_REGS_SIZE			16
+#define PUNIT_DEVICE_NAME		"intel_punit_ipc"
+#define TELEMETRY_DEVICE_NAME		"intel_telemetry"
+#define TELEM_SSRAM_SIZE		240
+#define TELEM_PMC_SSRAM_OFFSET		0x1B00
+#define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
+
+/* Commands */
+#define PMC_NORTHPEAK_CTRL		0xED
+
+/* PMC_CFG_REG bit masks */
+#define PMC_CFG_NO_REBOOT_EN		BIT(4)
+
+/* Index to cells array in below struct */
+enum {
+	PMC_TCO,
+	PMC_PUNIT,
+	PMC_TELEM,
+};
+
+struct intel_pmc_dev {
+	struct device *dev;
+	struct intel_scu_ipc_dev *scu;
+
+	struct mfd_cell cells[PMC_TELEM + 1];
+
+	void __iomem *gcr_mem_base;
+	spinlock_t gcr_lock;
+
+	struct resource *telem_base;
+};
+
+static inline bool is_gcr_valid(u32 offset)
+{
+	return offset < PLAT_RESOURCE_GCR_SIZE - 8;
+}
+
+/**
+ * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
+ * @pmc: PMC device pointer
+ * @offset: offset of GCR register from GCR address base
+ * @data: data pointer for storing the register output
+ *
+ * Reads the 64-bit PMC GCR register at given offset.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
+{
+	if (!is_gcr_valid(offset))
+		return -EINVAL;
+
+	spin_lock(&pmc->gcr_lock);
+	*data = readq(pmc->gcr_mem_base + offset);
+	spin_unlock(&pmc->gcr_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
+
+/**
+ * intel_pmc_gcr_update() - Update PMC GCR register bits
+ * @pmc: PMC device pointer
+ * @offset: offset of GCR register from GCR address base
+ * @mask: bit mask for update operation
+ * @val: update value
+ *
+ * Updates the bits of given GCR register as specified by
+ * @mask and @val.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+static int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask,
+				u32 val)
+{
+	u32 new_val;
+
+	if (!is_gcr_valid(offset))
+		return -EINVAL;
+
+	spin_lock(&pmc->gcr_lock);
+	new_val = readl(pmc->gcr_mem_base + offset);
+
+	new_val = (new_val & ~mask) | (val & mask);
+	writel(new_val, pmc->gcr_mem_base + offset);
+
+	new_val = readl(pmc->gcr_mem_base + offset);
+	spin_unlock(&pmc->gcr_lock);
+
+	/* Check whether the bit update is successful */
+	return (new_val & mask) != (val & mask) ? -EIO : 0;
+}
+
+/**
+ * intel_pmc_s0ix_counter_read() - Read S0ix residency.
+ * @pmc: PMC device pointer
+ * @data: Out param that contains current S0ix residency count.
+ *
+ * Writes to @data how many usecs the system has been in low-power S0ix
+ * state.
+ *
+ * Return: An error code or 0 on success.
+ */
+int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
+{
+	u64 deep, shlw;
+
+	spin_lock(&pmc->gcr_lock);
+	deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
+	shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
+	spin_unlock(&pmc->gcr_lock);
+
+	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
+
+static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
+	struct intel_scu_ipc_dev *scu = pmc->scu;
+	int subcmd;
+	int cmd;
+	int ret;
+
+	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+	if (ret != 2) {
+		dev_err(dev, "Invalid values, expected: cmd subcmd\n");
+		return -EINVAL;
+	}
+
+	ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
+	return ret ?: count;
+}
+static DEVICE_ATTR_WO(simplecmd);
+
+static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
+	struct intel_scu_ipc_dev *scu = pmc->scu;
+	unsigned long val;
+	int subcmd;
+	int ret;
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if (val)
+		subcmd = 1;
+	else
+		subcmd = 0;
+
+	ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
+	return ret ?: count;
+}
+static DEVICE_ATTR_WO(northpeak);
+
+static struct attribute *intel_pmc_attrs[] = {
+	&dev_attr_northpeak.attr,
+	&dev_attr_simplecmd.attr,
+	NULL
+};
+
+static const struct attribute_group intel_pmc_group = {
+	.attrs = intel_pmc_attrs,
+};
+
+static const struct attribute_group *intel_pmc_groups[] = {
+	&intel_pmc_group,
+	NULL
+};
+
+/* Templates used to construct MFD cells */
+
+static const struct mfd_cell punit = {
+	.name = PUNIT_DEVICE_NAME,
+};
+
+static int update_no_reboot_bit(void *priv, bool set)
+{
+	struct intel_pmc_dev *pmc = priv;
+	u32 bits = PMC_CFG_NO_REBOOT_EN;
+	u32 value = set ? bits : 0;
+
+	return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
+}
+
+static const struct itco_wdt_platform_data tco_pdata = {
+	.name = "Apollo Lake SoC",
+	.version = 5,
+	.update_no_reboot_bit = update_no_reboot_bit,
+};
+
+static const struct mfd_cell tco = {
+	.name = TCO_DEVICE_NAME,
+	.ignore_resource_conflicts = true,
+};
+
+static const struct resource telem_res[] = {
+	DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
+	DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
+};
+
+static const struct mfd_cell telem = {
+	.name = TELEMETRY_DEVICE_NAME,
+	.resources = telem_res,
+	.num_resources = ARRAY_SIZE(telem_res),
+};
+
+static int intel_pmc_get_tco_resources(struct platform_device *pdev,
+				       struct intel_pmc_dev *pmc)
+{
+	struct itco_wdt_platform_data *pdata;
+	struct resource *res, *tco_res;
+
+	if (acpi_has_watchdog())
+		return 0;
+
+	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 -EINVAL;
+	}
+
+	tco_res = devm_kcalloc(&pdev->dev, 2, sizeof(*tco_res), GFP_KERNEL);
+	if (!tco_res)
+		return -ENOMEM;
+
+	tco_res[0].flags = IORESOURCE_IO;
+	tco_res[0].start = res->start + TCO_BASE_OFFSET;
+	tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
+	tco_res[1].flags = IORESOURCE_IO;
+	tco_res[1].start = res->start + SMI_EN_OFFSET;
+	tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
+
+	pmc->cells[PMC_TCO].resources = tco_res;
+	pmc->cells[PMC_TCO].num_resources = 2;
+
+	pdata = devm_kmemdup(&pdev->dev, &tco_pdata, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdata->no_reboot_priv = pmc;
+	pmc->cells[PMC_TCO].platform_data = pdata;
+	pmc->cells[PMC_TCO].pdata_size = sizeof(*pdata);
+
+	return 0;
+}
+
+static int intel_pmc_get_resources(struct platform_device *pdev,
+				   struct intel_pmc_dev *pmc,
+				   struct intel_scu_ipc_pdata *pdata)
+{
+	struct resource *res, *punit_res;
+	struct resource gcr_res;
+	size_t npunit_res = 0;
+	int ret;
+
+	pdata->irq = platform_get_irq_optional(pdev, 0);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_IPC_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get IPC resource\n");
+		return -EINVAL;
+	}
+
+	/* IPC registers */
+	pdata->mem.flags = res->flags;
+	pdata->mem.start = res->start;
+	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
+
+	/* GCR registers */
+	gcr_res.flags = res->flags;
+	gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
+	gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
+
+	pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
+	if (IS_ERR(pmc->gcr_mem_base))
+		return PTR_ERR(pmc->gcr_mem_base);
+
+	pmc->cells[PMC_TCO] = tco;
+	pmc->cells[PMC_PUNIT] = punit;
+	pmc->cells[PMC_TELEM] = telem;
+
+	/* iTCO watchdog only if there is no WDAT ACPI table */
+	ret = intel_pmc_get_tco_resources(pdev, pmc);
+	if (ret)
+		return ret;
+
+	punit_res = devm_kcalloc(&pdev->dev, 6, sizeof(*punit_res), GFP_KERNEL);
+	if (!punit_res)
+		return -ENOMEM;
+
+	/* 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 resource of P-unit BIOS data\n");
+		return -EINVAL;
+	}
+	punit_res[npunit_res++] = *res;
+
+	/* This is index 1 to cover BIOS interface register */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
+		return -EINVAL;
+	}
+	punit_res[npunit_res++] = *res;
+
+	/* This is index 2 to cover ISP data register, optional */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_ISP_DATA_INDEX);
+	if (res)
+		punit_res[npunit_res++] = *res;
+
+	/* This is index 3 to cover ISP interface register, optional */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_ISP_IFACE_INDEX);
+	if (res)
+		punit_res[npunit_res++] = *res;
+
+	/* This is index 4 to cover GTD data register, optional */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_GTD_DATA_INDEX);
+	if (res)
+		punit_res[npunit_res++] = *res;
+
+	/* This is index 5 to cover GTD interface register, optional */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_GTD_IFACE_INDEX);
+	if (res)
+		punit_res[npunit_res++] = *res;
+
+	pmc->cells[PMC_PUNIT].resources = punit_res;
+	pmc->cells[PMC_PUNIT].num_resources = npunit_res;
+
+	/* Telemetry SSRAM is optional */
+	res = platform_get_resource(pdev, IORESOURCE_MEM,
+				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
+	if (res)
+		pmc->telem_base = res;
+
+	return 0;
+}
+
+static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
+{
+	int ret;
+
+	if (pmc->cells[PMC_TCO].num_resources) {
+		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
+					   &pmc->cells[PMC_TCO], 1, NULL, 0, NULL);
+		if (ret)
+			return ret;
+	}
+
+	ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
+				   &pmc->cells[PMC_PUNIT], 1, NULL, 0, NULL);
+	if (ret)
+		return ret;
+
+	if (pmc->telem_base) {
+		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
+					   &pmc->cells[PMC_TELEM], 1,
+					   pmc->telem_base, 0, NULL);
+	}
+
+	return ret;
+}
+
+static const struct acpi_device_id intel_pmc_acpi_ids[] = {
+	{ "INT34D2" },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
+
+static int intel_pmc_probe(struct platform_device *pdev)
+{
+	struct intel_scu_ipc_pdata pdata = {};
+	struct intel_pmc_dev *pmc;
+	int ret;
+
+	pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+	if (!pmc)
+		return -ENOMEM;
+
+	pmc->dev = &pdev->dev;
+	spin_lock_init(&pmc->gcr_lock);
+
+	ret = intel_pmc_get_resources(pdev, pmc, &pdata);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request resources\n");
+		return ret;
+	}
+
+	pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
+	if (IS_ERR(pmc->scu))
+		return PTR_ERR(pmc->scu);
+
+	platform_set_drvdata(pdev, pmc);
+
+	ret = intel_pmc_create_devices(pmc);
+	if (ret)
+		dev_err(&pdev->dev, "Failed to create PMC devices\n");
+
+	return ret;
+}
+
+static struct platform_driver intel_pmc_driver = {
+	.probe = intel_pmc_probe,
+	.driver = {
+		.name = "intel_pmc_bxt",
+		.acpi_match_table = intel_pmc_acpi_ids,
+		.dev_groups = intel_pmc_groups,
+	},
+};
+
+module_platform_driver(intel_pmc_driver);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
+MODULE_DESCRIPTION("Intel Broxton PMC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 65eb53fc9206..91acc99a48bc 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1261,7 +1261,8 @@ config INTEL_UNCORE_FREQ_CONTROL
 config INTEL_BXTWC_PMIC_TMU
 	tristate "Intel BXT Whiskey Cove TMU Driver"
 	depends on REGMAP
-	depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
+	depends on MFD_INTEL_PMC_BXT
+	depends on INTEL_SOC_PMIC_BXTWC
 	---help---
 	  Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
 	  This driver enables the alarm wakeup functionality in the TMU unit
@@ -1319,15 +1320,6 @@ config INTEL_PMC_CORE
 		- LTR Ignore
 		- MPHY/PLL gating status (Sunrisepoint PCH only)
 
-config INTEL_PMC_IPC
-	tristate "Intel PMC IPC Driver"
-	depends on ACPI
-	select INTEL_SCU_IPC
-	---help---
-	This driver provides support for PMC control on some Intel platforms.
-	The PMC is an ARC processor which defines IPC commands for communication
-	with other entities in the CPU.
-
 config INTEL_PUNIT_IPC
 	tristate "Intel P-Unit IPC Driver"
 	---help---
@@ -1366,7 +1358,9 @@ config INTEL_SCU_IPC_UTIL
 
 config INTEL_TELEMETRY
 	tristate "Intel SoC Telemetry Driver"
-	depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
+	depends on X86_64
+	depends on MFD_INTEL_PMC_BXT
+	depends on INTEL_PUNIT_IPC
 	---help---
 	  This driver provides interfaces to configure and use
 	  telemetry for INTEL SoC from APL onwards. It is also
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index a750bd8c1e81..193c77f71c33 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -137,7 +137,6 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL)	+= intel_mid_thermal.o
 obj-$(CONFIG_INTEL_MID_POWER_BUTTON)	+= intel_mid_powerbtn.o
 obj-$(CONFIG_INTEL_MRFLD_PWRBTN)	+= intel_mrfld_pwrbtn.o
 obj-$(CONFIG_INTEL_PMC_CORE)		+= intel_pmc_core.o intel_pmc_core_pltdrv.o
-obj-$(CONFIG_INTEL_PMC_IPC)		+= intel_pmc_ipc.o
 obj-$(CONFIG_INTEL_PUNIT_IPC)		+= intel_punit_ipc.o
 obj-$(CONFIG_INTEL_SCU_IPC)		+= intel_scu_ipc.o
 obj-$(CONFIG_INTEL_SCU_PCI)		+= intel_scu_pcidrv.o
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
deleted file mode 100644
index c006609ef74b..000000000000
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ /dev/null
@@ -1,645 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Driver for the Intel PMC IPC mechanism
- *
- * (C) Copyright 2014-2015 Intel Corporation
- *
- * This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by
- *     Sreedhara DS <sreedhara.ds@intel.com>
- *
- * PMC running in ARC processor communicates with other entity running in IA
- * core through IPC mechanism which in turn messaging between IA core ad PMC.
- */
-
-#include <linux/acpi.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/io-64-nonatomic-lo-hi.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include <asm/intel_pmc_ipc.h>
-#include <asm/intel_scu_ipc.h>
-
-#include <linux/platform_data/itco_wdt.h>
-
-/* Residency with clock rate at 19.2MHz to usecs */
-#define S0IX_RESIDENCY_IN_USECS(d, s)		\
-({						\
-	u64 result = 10ull * ((d) + (s));	\
-	do_div(result, 192);			\
-	result;					\
-})
-
-/* exported resources from IFWI */
-#define PLAT_RESOURCE_IPC_INDEX		0
-#define PLAT_RESOURCE_IPC_SIZE		0x1000
-#define PLAT_RESOURCE_GCR_OFFSET	0x1000
-#define PLAT_RESOURCE_GCR_SIZE		0x1000
-#define PLAT_RESOURCE_BIOS_DATA_INDEX	1
-#define PLAT_RESOURCE_BIOS_IFACE_INDEX	2
-#define PLAT_RESOURCE_TELEM_SSRAM_INDEX	3
-#define PLAT_RESOURCE_ISP_DATA_INDEX	4
-#define PLAT_RESOURCE_ISP_IFACE_INDEX	5
-#define PLAT_RESOURCE_GTD_DATA_INDEX	6
-#define PLAT_RESOURCE_GTD_IFACE_INDEX	7
-#define PLAT_RESOURCE_ACPI_IO_INDEX	0
-
-/*
- * BIOS does not create an ACPI device for each PMC function,
- * but exports multiple resources from one ACPI device(IPC) for
- * multiple functions. This driver is responsible to create a
- * platform device and to export resources for those functions.
- */
-#define TCO_DEVICE_NAME			"iTCO_wdt"
-#define SMI_EN_OFFSET			0x40
-#define SMI_EN_SIZE			4
-#define TCO_BASE_OFFSET			0x60
-#define TCO_REGS_SIZE			16
-#define PUNIT_DEVICE_NAME		"intel_punit_ipc"
-#define TELEMETRY_DEVICE_NAME		"intel_telemetry"
-#define TELEM_SSRAM_SIZE		240
-#define TELEM_PMC_SSRAM_OFFSET		0x1B00
-#define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x08
-#define TCO_PMC_SIZE			0x04
-
-/* PMC register bit definitions */
-
-/* PMC_CFG_REG bit masks */
-#define PMC_CFG_NO_REBOOT_MASK		BIT_MASK(4)
-#define PMC_CFG_NO_REBOOT_EN		(1 << 4)
-#define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
-
-static struct intel_pmc_ipc_dev {
-	struct device *dev;
-
-	/* 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;
-	unsigned int punit_res_count;
-
-	/* 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 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_read64() - Read a 64-bit PMC GCR register
- * @offset:	offset of GCR register from GCR address base
- * @data:	data pointer for storing the register output
- *
- * Reads the 64-bit PMC GCR register at given offset.
- *
- * Return:	negative value on error or 0 on success.
- */
-int intel_pmc_gcr_read64(u32 offset, u64 *data)
-{
-	int ret;
-
-	spin_lock(&ipcdev.gcr_lock);
-
-	ret = is_gcr_valid(offset);
-	if (ret < 0) {
-		spin_unlock(&ipcdev.gcr_lock);
-		return ret;
-	}
-
-	*data = readq(ipcdev.gcr_mem_base + offset);
-
-	spin_unlock(&ipcdev.gcr_lock);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
-
-/**
- * intel_pmc_gcr_update() - Update PMC GCR register bits
- * @offset:	offset of GCR register from GCR address base
- * @mask:	bit mask for update operation
- * @val:	update value
- *
- * Updates the bits of given GCR register as specified by
- * @mask and @val.
- *
- * Return:	negative value on error or 0 on success.
- */
-static 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);
-
-	/* check whether the bit update is successful */
-	if ((new_val & mask) != (val & mask)) {
-		ret = -EIO;
-		goto gcr_ipc_unlock;
-	}
-
-gcr_ipc_unlock:
-	spin_unlock(&ipcdev.gcr_lock);
-	return ret;
-}
-
-static int update_no_reboot_bit(void *priv, bool set)
-{
-	u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
-
-	return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
-				    PMC_CFG_NO_REBOOT_MASK, value);
-}
-
-static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
-					      struct device_attribute *attr,
-					      const char *buf, size_t count)
-{
-	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
-	int subcmd;
-	int cmd;
-	int ret;
-
-	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
-	if (ret != 2) {
-		dev_err(dev, "Error args\n");
-		return -EINVAL;
-	}
-
-	ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
-	if (ret) {
-		dev_err(dev, "command %d error with %d\n", cmd, ret);
-		return ret;
-	}
-	return (ssize_t)count;
-}
-static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store);
-
-static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
-					     struct device_attribute *attr,
-					     const char *buf, size_t count)
-{
-	struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
-	unsigned long val;
-	int subcmd;
-	int ret;
-
-	ret = kstrtoul(buf, 0, &val);
-	if (ret)
-		return ret;
-
-	if (val)
-		subcmd = 1;
-	else
-		subcmd = 0;
-	ret = intel_scu_ipc_dev_simple_command(scu, PMC_IPC_NORTHPEAK_CTRL, subcmd);
-	if (ret) {
-		dev_err(dev, "command north %d error with %d\n", subcmd, ret);
-		return ret;
-	}
-	return (ssize_t)count;
-}
-static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store);
-
-static struct attribute *intel_ipc_attrs[] = {
-	&dev_attr_northpeak.attr,
-	&dev_attr_simplecmd.attr,
-	NULL
-};
-
-static const struct attribute_group intel_ipc_group = {
-	.attrs = intel_ipc_attrs,
-};
-
-static const struct attribute_group *intel_ipc_groups[] = {
-	&intel_ipc_group,
-	NULL
-};
-
-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,
-	.no_reboot_priv = &ipcdev,
-	.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)
-{
-	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 = ipcdev.punit_res_count,
-		};
-
-	pdev = platform_device_register_full(&pdevinfo);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	ipcdev.punit_dev = pdev;
-
-	return 0;
-}
-
-static int ipc_create_tco_device(void)
-{
-	struct platform_device *pdev;
-	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),
-		};
-
-	res = tco_res + TCO_RESOURCE_ACPI_IO;
-	res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
-	res->end = res->start + TCO_REGS_SIZE - 1;
-
-	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;
-
-	return 0;
-}
-
-static int ipc_create_telemetry_device(void)
-{
-	struct platform_device *pdev;
-	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);
-
-	ipcdev.telemetry_dev = pdev;
-
-	return 0;
-}
-
-static int ipc_create_pmc_devices(void)
-{
-	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();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-		platform_device_unregister(ipcdev.tco_dev);
-		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");
-			platform_device_unregister(ipcdev.punit_dev);
-			platform_device_unregister(ipcdev.tco_dev);
-		}
-	}
-
-	return ret;
-}
-
-static int ipc_plat_get_res(struct platform_device *pdev,
-			    struct intel_scu_ipc_pdata *pdata)
-{
-	struct resource *res, *punit_res = punit_res_array;
-	resource_size_t start;
-	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);
-
-	ipcdev.punit_res_count = 0;
-
-	/* 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[ipcdev.punit_res_count++] = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-	/* This is index 1 to cover BIOS interface register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
-		return -ENXIO;
-	}
-	punit_res[ipcdev.punit_res_count++] = *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);
-	if (res) {
-		punit_res[ipcdev.punit_res_count++] = *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);
-	if (res) {
-		punit_res[ipcdev.punit_res_count++] = *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);
-	if (res) {
-		punit_res[ipcdev.punit_res_count++] = *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);
-	if (res) {
-		punit_res[ipcdev.punit_res_count++] = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
-	}
-
-	pdata->irq = platform_get_irq(pdev, 0);
-
-	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;
-	}
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	pdata->mem.flags = res->flags;
-	pdata->mem.start = res->start;
-	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
-
-	start = res->start + PLAT_RESOURCE_GCR_OFFSET;
-	if (!devm_request_mem_region(&pdev->dev, start, PLAT_RESOURCE_GCR_SIZE,
-				     "pmc_ipc_plat"))
-		return -EBUSY;
-
-	addr = devm_ioremap(&pdev->dev, start, PLAT_RESOURCE_GCR_SIZE);
-	if (!addr)
-		return -ENOMEM;
-
-	ipcdev.gcr_mem_base = addr;
-
-	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);
-	}
-
-	return 0;
-}
-
-/**
- * intel_pmc_s0ix_counter_read() - Read S0ix residency.
- * @data: Out param that contains current S0ix residency count.
- *
- * Return: an error code or 0 on success.
- */
-int intel_pmc_s0ix_counter_read(u64 *data)
-{
-	u64 deep, shlw;
-
-	if (!ipcdev.has_gcr_regs)
-		return -EACCES;
-
-	deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
-	shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
-
-	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id ipc_acpi_ids[] = {
-	{ "INT34D2", 0},
-	{ }
-};
-MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
-#endif
-
-static int ipc_plat_probe(struct platform_device *pdev)
-{
-	struct intel_scu_ipc_pdata pdata = {};
-	struct intel_scu_ipc_dev *scu;
-	int ret;
-
-	ipcdev.dev = &pdev->dev;
-	spin_lock_init(&ipcdev.gcr_lock);
-
-	ret = ipc_plat_get_res(pdev, &pdata);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request resource\n");
-		return ret;
-	}
-
-	scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
-	if (IS_ERR(scu))
-		return PTR_ERR(scu);
-
-	platform_set_drvdata(pdev, scu);
-
-	ret = ipc_create_pmc_devices();
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to create pmc devices\n");
-		return ret;
-	}
-
-	ipcdev.has_gcr_regs = true;
-
-	return 0;
-}
-
-static int ipc_plat_remove(struct platform_device *pdev)
-{
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-	ipcdev.dev = NULL;
-	return 0;
-}
-
-static struct platform_driver ipc_plat_driver = {
-	.remove = ipc_plat_remove,
-	.probe = ipc_plat_probe,
-	.driver = {
-		.name = "pmc-ipc-plat",
-		.acpi_match_table = ACPI_PTR(ipc_acpi_ids),
-		.dev_groups = intel_ipc_groups,
-	},
-};
-
-static int __init intel_pmc_ipc_init(void)
-{
-	return platform_driver_register(&ipc_plat_driver);
-}
-
-static void __exit intel_pmc_ipc_exit(void)
-{
-	platform_driver_unregister(&ipc_plat_driver);
-}
-
-MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
-MODULE_DESCRIPTION("Intel PMC IPC driver");
-MODULE_LICENSE("GPL v2");
-
-/* Some modules are dependent on this, so init earlier */
-fs_initcall(intel_pmc_ipc_init);
-module_exit(intel_pmc_ipc_exit);
diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c
index 85a456aa0ab9..3a533fa22760 100644
--- a/drivers/platform/x86/intel_telemetry_debugfs.c
+++ b/drivers/platform/x86/intel_telemetry_debugfs.c
@@ -15,6 +15,7 @@
  */
 #include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/mfd/intel_pmc_bxt.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/seq_file.h>
@@ -22,7 +23,6 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/intel_pmc_ipc.h>
 #include <asm/intel_telemetry.h>
 
 #define DRIVER_NAME			"telemetry_soc_debugfs"
@@ -648,10 +648,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states);
 
 static int telem_s0ix_res_get(void *data, u64 *val)
 {
+	struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
 	u64 s0ix_total_res;
 	int ret;
 
-	ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
+	ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res);
 	if (ret) {
 		pr_err("Failed to read S0ix residency");
 		return ret;
@@ -838,12 +839,15 @@ static int pm_suspend_exit_cb(void)
 	 */
 	if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
 	    suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
-		ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
+		struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
+		struct intel_pmc_dev *pmc = plt_config->pmc;
+
+		ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG,
 					  &suspend_shlw_res_exit);
 		if (ret < 0)
 			goto out;
 
-		ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
+		ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG,
 					  &suspend_deep_res_exit);
 		if (ret < 0)
 			goto out;
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index efcf214d25b1..d2aee90fb0c5 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -1118,6 +1118,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 
 	telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
+	telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
+
 	mem = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(mem))
 		return PTR_ERR(mem);
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index 5b986d6c801d..fa3f39336246 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -41,8 +41,8 @@ config TYPEC_FUSB302
 config TYPEC_WCOVE
 	tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
 	depends on ACPI
+	depends on MFD_INTEL_PMC_BXT
 	depends on INTEL_SOC_PMIC
-	depends on INTEL_PMC_IPC
 	depends on BXT_WC_PMIC_OPREGION
 	help
 	  This driver adds support for USB Type-C on Intel Broxton platforms
diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
new file mode 100644
index 000000000000..a5fb41910d78
--- /dev/null
+++ b/include/linux/mfd/intel_pmc_bxt.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef MFD_INTEL_PMC_BXT_H
+#define MFD_INTEL_PMC_BXT_H
+
+#include <linux/types.h>
+
+/* GCR reg offsets from GCR base */
+#define PMC_GCR_PMC_CFG_REG		0x08
+#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
+#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
+
+/*
+ * Pointer to the PMC device can be obtained by calling
+ * dev_get_drvdata() to the parent MFD device.
+ */
+struct intel_pmc_dev;
+
+int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
+int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
+
+#endif /* MFD_INTEL_PMC_BXT_H */
-- 
2.25.0


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

* [PATCH v6 19/19] MAINTAINERS: Update entry for Intel Broxton PMC driver
  2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
                   ` (17 preceding siblings ...)
  2020-02-17 13:14 ` [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD Mika Westerberg
@ 2020-02-17 13:14 ` Mika Westerberg
  18 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-17 13:14 UTC (permalink / raw)
  To: Andy Shevchenko, Darren Hart, Lee Jones, Greg Kroah-Hartman
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, Mika Westerberg, platform-driver-x86,
	linux-kernel

The driver lives now under MFD so split the current entry into two parts
and add me as co-maintainer of the Intel Broxton PMC driver. While there
correct formatting of Zha Qipeng's email address.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 MAINTAINERS | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 4d4e3241bf99..20e4353260f7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8371,6 +8371,13 @@ L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/intel_atomisp2_pm.c
 
+INTEL BROXTON PMC DRIVER
+M:	Mika Westerberg <mika.westerberg@linux.intel.com>
+M:	Zha Qipeng <qipeng.zha@intel.com>
+S:	Maintained
+F:	drivers/mfd/intel_pmc_bxt.c
+F:	include/linux/mfd/intel_pmc_bxt.h
+
 INTEL C600 SERIES SAS CONTROLLER DRIVER
 M:	Intel SCU Linux support <intel-linux-scu@intel.com>
 M:	Artur Paszkiewicz <artur.paszkiewicz@intel.com>
@@ -8579,6 +8586,13 @@ F:	drivers/dma/mic_x100_dma.c
 F:	drivers/dma/mic_x100_dma.h
 F:	Documentation/mic/
 
+INTEL P-Unit IPC DRIVER
+M:	Zha Qipeng <qipeng.zha@intel.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	arch/x86/include/asm/intel_punit_ipc.h
+F:	drivers/platform/x86/intel_punit_ipc.c
+
 INTEL PMC CORE DRIVER
 M:	Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
 M:	Vishwanath Somayaji <vishwanath.somayaji@intel.com>
@@ -8586,15 +8600,6 @@ L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/intel_pmc_core*
 
-INTEL PMC/P-Unit IPC DRIVER
-M:	Zha Qipeng<qipeng.zha@intel.com>
-L:	platform-driver-x86@vger.kernel.org
-S:	Maintained
-F:	arch/x86/include/asm/intel_pmc_ipc.h
-F:	arch/x86/include/asm/intel_punit_ipc.h
-F:	drivers/platform/x86/intel_pmc_ipc.c
-F:	drivers/platform/x86/intel_punit_ipc.c
-
 INTEL PMIC GPIO DRIVERS
 M:	Andy Shevchenko <andy@kernel.org>
 S:	Maintained
-- 
2.25.0


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

* Re: [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails
  2020-02-17 13:14 ` [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails Mika Westerberg
@ 2020-02-17 14:08   ` Andy Shevchenko
  0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2020-02-17 14:08 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Darren Hart, Lee Jones, Greg Kroah-Hartman, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, H . Peter Anvin, x86, Zha Qipeng,
	David E . Box, Guenter Roeck, Heikki Krogerus, Wim Van Sebroeck,
	platform-driver-x86, linux-kernel

On Mon, Feb 17, 2020 at 04:14:29PM +0300, Mika Westerberg wrote:
> Currently we only log an error if the command times out which makes it
> hard to figure out the failing command. This changes the driver to log
> command and subcommand with the error code which should make debugging
> easier. This also allows us to simplify the callers as they don't need
> to log these errors themselves.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> ---
>  drivers/platform/x86/intel_scu_ipc.c | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
> index 19c2cc41fb05..7512d550b375 100644
> --- a/drivers/platform/x86/intel_scu_ipc.c
> +++ b/drivers/platform/x86/intel_scu_ipc.c
> @@ -147,7 +147,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
>  		usleep_range(50, 100);
>  	} while (time_before(jiffies, end));
>  
> -	dev_err(&scu->dev, "IPC timed out");
>  	return -ETIMEDOUT;
>  }
>  
> @@ -156,10 +155,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
>  {
>  	int status;
>  
> -	if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
> -		dev_err(&scu->dev, "IPC timed out\n");
> +	if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
>  		return -ETIMEDOUT;
> -	}
>  
>  	status = ipc_read_status(scu);
>  	if (status & IPC_STATUS_ERR)
> @@ -331,6 +328,7 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
>  int intel_scu_ipc_simple_command(int cmd, int sub)
>  {
>  	struct intel_scu_ipc_dev *scu;
> +	u32 cmdval;
>  	int err;
>  
>  	mutex_lock(&ipclock);
> @@ -339,9 +337,12 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
>  		return -ENODEV;
>  	}
>  	scu = ipcdev;
> -	ipc_command(scu, sub << 12 | cmd);
> +	cmdval = sub << 12 | cmd;
> +	ipc_command(scu, cmdval);
>  	err = intel_scu_ipc_check_status(scu);
>  	mutex_unlock(&ipclock);
> +	if (err)
> +		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
>  	return err;
>  }
>  EXPORT_SYMBOL(intel_scu_ipc_simple_command);
> @@ -362,6 +363,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
>  			  u32 *out, int outlen)
>  {
>  	struct intel_scu_ipc_dev *scu;
> +	u32 cmdval;
>  	int i, err;
>  
>  	mutex_lock(&ipclock);
> @@ -374,7 +376,8 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
>  	for (i = 0; i < inlen; i++)
>  		ipc_data_writel(scu, *in++, 4 * i);
>  
> -	ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
> +	cmdval = (inlen << 16) | (sub << 12) | cmd;
> +	ipc_command(scu, cmdval);
>  	err = intel_scu_ipc_check_status(scu);
>  
>  	if (!err) {
> @@ -383,6 +386,8 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
>  	}
>  
>  	mutex_unlock(&ipclock);
> +	if (err)
> +		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
>  	return err;
>  }
>  EXPORT_SYMBOL(intel_scu_ipc_command);
> -- 
> 2.25.0
> 

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header
  2020-02-17 13:14 ` [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header Mika Westerberg
@ 2020-02-17 14:11   ` Andy Shevchenko
  0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2020-02-17 14:11 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Darren Hart, Lee Jones, Greg Kroah-Hartman, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, H . Peter Anvin, x86, Zha Qipeng,
	David E . Box, Guenter Roeck, Heikki Krogerus, Wim Van Sebroeck,
	platform-driver-x86, linux-kernel

On Mon, Feb 17, 2020 at 04:14:30PM +0300, Mika Westerberg wrote:
> In preparation for introducing a new API for SCU IPC, move the legacy
> API and constants to a separate header that is is subject to be removed
> eventually.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_scu_ipc.h        | 56 +------------------
>  arch/x86/include/asm/intel_scu_ipc_legacy.h | 62 +++++++++++++++++++++
>  2 files changed, 63 insertions(+), 55 deletions(-)
>  create mode 100644 arch/x86/include/asm/intel_scu_ipc_legacy.h
> 
> diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
> index 78f939782a67..a2f4694a253c 100644
> --- a/arch/x86/include/asm/intel_scu_ipc.h
> +++ b/arch/x86/include/asm/intel_scu_ipc.h
> @@ -3,22 +3,6 @@
>  #define  _ASM_X86_INTEL_SCU_IPC_H_
>  
>  #include <linux/ioport.h>
> -#include <linux/notifier.h>
> -
> -#define IPCMSG_INDIRECT_READ	0x02
> -#define IPCMSG_INDIRECT_WRITE	0x05
> -
> -#define IPCMSG_COLD_OFF		0x80	/* Only for Tangier */
> -
> -#define IPCMSG_WARM_RESET	0xF0
> -#define IPCMSG_COLD_RESET	0xF1
> -#define IPCMSG_SOFT_RESET	0xF2
> -#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 */
>  
>  struct device;
>  struct intel_scu_ipc_dev;
> @@ -37,44 +21,6 @@ struct intel_scu_ipc_dev *
>  intel_scu_ipc_register(struct device *parent,
>  		       const struct intel_scu_ipc_pdata *pdata);
>  
> -/* Read single register */
> -int intel_scu_ipc_ioread8(u16 addr, u8 *data);
> -
> -/* Read a vector */
> -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
> -
> -/* Write single register */
> -int intel_scu_ipc_iowrite8(u16 addr, u8 data);
> -
> -/* Write a vector */
> -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);
> -
> -extern struct blocking_notifier_head intel_scu_notifier;
> -
> -static inline void intel_scu_notifier_add(struct notifier_block *nb)
> -{
> -	blocking_notifier_chain_register(&intel_scu_notifier, nb);
> -}
> -
> -static inline void intel_scu_notifier_remove(struct notifier_block *nb)
> -{
> -	blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
> -}
> -
> -static inline int intel_scu_notifier_post(unsigned long v, void *p)
> -{
> -	return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
> -}
> -
> -#define		SCU_AVAILABLE		1
> -#define		SCU_DOWN		2
> +#include <asm/intel_scu_ipc_legacy.h>
>  
>  #endif
> diff --git a/arch/x86/include/asm/intel_scu_ipc_legacy.h b/arch/x86/include/asm/intel_scu_ipc_legacy.h
> new file mode 100644
> index 000000000000..d3a02bc07edd
> --- /dev/null
> +++ b/arch/x86/include/asm/intel_scu_ipc_legacy.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
> +#define _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
> +
> +#include <linux/notifier.h>
> +
> +#define IPCMSG_INDIRECT_READ	0x02
> +#define IPCMSG_INDIRECT_WRITE	0x05
> +
> +#define IPCMSG_COLD_OFF		0x80	/* Only for Tangier */
> +
> +#define IPCMSG_WARM_RESET	0xF0
> +#define IPCMSG_COLD_RESET	0xF1
> +#define IPCMSG_SOFT_RESET	0xF2
> +#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 */
> +
> +/* Read single register */
> +int intel_scu_ipc_ioread8(u16 addr, u8 *data);
> +
> +/* Read a vector */
> +int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
> +
> +/* Write single register */
> +int intel_scu_ipc_iowrite8(u16 addr, u8 data);
> +
> +/* Write a vector */
> +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);
> +
> +extern struct blocking_notifier_head intel_scu_notifier;
> +
> +static inline void intel_scu_notifier_add(struct notifier_block *nb)
> +{
> +	blocking_notifier_chain_register(&intel_scu_notifier, nb);
> +}
> +
> +static inline void intel_scu_notifier_remove(struct notifier_block *nb)
> +{
> +	blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
> +}
> +
> +static inline int intel_scu_notifier_post(unsigned long v, void *p)
> +{
> +	return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
> +}
> +
> +#define		SCU_AVAILABLE		1
> +#define		SCU_DOWN		2
> +
> +#endif
> -- 
> 2.25.0
> 

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API
  2020-02-17 13:14 ` [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API Mika Westerberg
@ 2020-02-17 14:11   ` Andy Shevchenko
  0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2020-02-17 14:11 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Darren Hart, Lee Jones, Greg Kroah-Hartman, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, H . Peter Anvin, x86, Zha Qipeng,
	David E . Box, Guenter Roeck, Heikki Krogerus, Wim Van Sebroeck,
	platform-driver-x86, linux-kernel

On Mon, Feb 17, 2020 at 04:14:31PM +0300, Mika Westerberg wrote:
> The current SCU IPC API has been operating on a single instance and
> there has been no way to pin the providing module in place when the SCU
> IPC is in use.
> 
> This implements a new API that takes the SCU IPC instance as first
> parameter (NULL means the single instance is being used). The SCU IPC
> instance can be retrieved by calling new function intel_scu_ipc_dev_get()
> that take care of pinning the providing module in place as long as
> intel_scu_ipc_dev_put() is not called.
> 
> The old API is updated to call the new API and is is left there in the
> legacy API header to support the existing users that cannot be converted
> easily.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

> Subsequent patches will convert most of the users over to the new API.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_scu_ipc.h        |  38 +++-
>  arch/x86/include/asm/intel_scu_ipc_legacy.h |  45 +++-
>  drivers/platform/x86/intel_scu_ipc.c        | 230 +++++++++++++++-----
>  3 files changed, 252 insertions(+), 61 deletions(-)
> 
> diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
> index a2f4694a253c..27041fe999e9 100644
> --- a/arch/x86/include/asm/intel_scu_ipc.h
> +++ b/arch/x86/include/asm/intel_scu_ipc.h
> @@ -18,8 +18,42 @@ struct intel_scu_ipc_pdata {
>  };
>  
>  struct intel_scu_ipc_dev *
> -intel_scu_ipc_register(struct device *parent,
> -		       const struct intel_scu_ipc_pdata *pdata);
> +__intel_scu_ipc_register(struct device *parent,
> +			 const struct intel_scu_ipc_pdata *pdata,
> +			 struct module *owner);
> +
> +#define intel_scu_ipc_register(parent, pdata)  \
> +	__intel_scu_ipc_register(parent, pdata, THIS_MODULE)
> +
> +struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
> +void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
> +struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
> +
> +int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
> +			      u8 *data);
> +int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr,
> +			       u8 data);
> +int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr,
> +			    u8 *data, size_t len);
> +int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr,
> +			     u8 *data, size_t len);
> +
> +int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
> +			     u8 data, u8 mask);
> +
> +int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
> +				     int sub);
> +int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
> +					int sub, const void *in, size_t inlen,
> +					size_t size, void *out, size_t outlen);
> +
> +static inline int intel_scu_ipc_dev_command(struct intel_scu_ipc_dev *scu, int cmd,
> +					    int sub, const void *in, size_t inlen,
> +					    void *out, size_t outlen)
> +{
> +	return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
> +						   inlen, out, outlen);
> +}
>  
>  #include <asm/intel_scu_ipc_legacy.h>
>  
> diff --git a/arch/x86/include/asm/intel_scu_ipc_legacy.h b/arch/x86/include/asm/intel_scu_ipc_legacy.h
> index d3a02bc07edd..4cf13fecb673 100644
> --- a/arch/x86/include/asm/intel_scu_ipc_legacy.h
> +++ b/arch/x86/include/asm/intel_scu_ipc_legacy.h
> @@ -19,25 +19,54 @@
>  #define IPC_CMD_VRTC_SETTIME      1	/* Set time */
>  #define IPC_CMD_VRTC_SETALARM     2	/* Set alarm */
>  
> +/* Don't call these in new code - they will be removed eventually */
> +
>  /* Read single register */
> -int intel_scu_ipc_ioread8(u16 addr, u8 *data);
> +static inline int intel_scu_ipc_ioread8(u16 addr, u8 *data)
> +{
> +	return intel_scu_ipc_dev_ioread8(NULL, addr, data);
> +}
>  
>  /* Read a vector */
> -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
> +static inline int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
> +{
> +	return intel_scu_ipc_dev_readv(NULL, addr, data, len);
> +}
>  
>  /* Write single register */
> -int intel_scu_ipc_iowrite8(u16 addr, u8 data);
> +static inline int intel_scu_ipc_iowrite8(u16 addr, u8 data)
> +{
> +	return intel_scu_ipc_dev_iowrite8(NULL, addr, data);
> +}
>  
>  /* Write a vector */
> -int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
> +static inline int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
> +{
> +	return intel_scu_ipc_dev_writev(NULL, addr, data, len);
> +}
>  
>  /* Update single register based on the mask */
> -int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
> +static inline int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask)
> +{
> +	return intel_scu_ipc_dev_update(NULL, addr, data, 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);
> +static inline int intel_scu_ipc_simple_command(int cmd, int sub)
> +{
> +	return intel_scu_ipc_dev_simple_command(NULL, cmd, sub);
> +}
> +
> +static inline int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
> +					u32 *out, int outlen)
> +{
> +	/* New API takes both inlen and outlen as bytes so convert here */
> +	size_t inbytes = inlen * sizeof(u32);
> +	size_t outbytes = outlen * sizeof(u32);
> +
> +	return intel_scu_ipc_dev_command_with_size(NULL, cmd, sub, in, inbytes,
> +						   inlen, out, outbytes);
> +}
>  
>  extern struct blocking_notifier_head intel_scu_notifier;
>  
> diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
> index 7512d550b375..a2f05b4dd2b9 100644
> --- a/drivers/platform/x86/intel_scu_ipc.c
> +++ b/drivers/platform/x86/intel_scu_ipc.c
> @@ -56,6 +56,7 @@
>  struct intel_scu_ipc_dev {
>  	struct device dev;
>  	struct resource mem;
> +	struct module *owner;
>  	int irq;
>  	void __iomem *ipc_base;
>  	struct completion cmd_complete;
> @@ -84,6 +85,102 @@ static struct class intel_scu_ipc_class = {
>  	.owner = THIS_MODULE,
>  };
>  
> +/**
> + * intel_scu_ipc_dev_get() - Get SCU IPC instance
> + *
> + * The recommended new API takes SCU IPC instance as parameter and this
> + * function can be called by driver to get the instance. This also makes
> + * sure the driver providing the IPC functionality cannot be unloaded
> + * while the caller has the instance.
> + *
> + * Call intel_scu_ipc_dev_put() to release the instance.
> + *
> + * Returns %NULL if SCU IPC is not currently available.
> + */
> +struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
> +{
> +	struct intel_scu_ipc_dev *scu = NULL;
> +
> +	mutex_lock(&ipclock);
> +	if (ipcdev) {
> +		get_device(&ipcdev->dev);
> +		/*
> +		 * Prevent the IPC provider from being unloaded while it
> +		 * is being used.
> +		 */
> +		if (!try_module_get(ipcdev->owner))
> +			put_device(&ipcdev->dev);
> +		else
> +			scu = ipcdev;
> +	}
> +
> +	mutex_unlock(&ipclock);
> +	return scu;
> +}
> +EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
> +
> +/**
> + * intel_scu_ipc_dev_put() - Put SCU IPC instance
> + * @scu: SCU IPC instance
> + *
> + * This function releases the SCU IPC instance retrieved from
> + * intel_scu_ipc_dev_get() and allows the driver providing IPC to be
> + * unloaded.
> + */
> +void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
> +{
> +	if (scu) {
> +		module_put(scu->owner);
> +		put_device(&scu->dev);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
> +
> +struct intel_scu_ipc_devres {
> +	struct intel_scu_ipc_dev *scu;
> +};
> +
> +static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
> +{
> +	struct intel_scu_ipc_devres *dr = res;
> +	struct intel_scu_ipc_dev *scu = dr->scu;
> +
> +	intel_scu_ipc_dev_put(scu);
> +}
> +
> +/**
> + * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
> + * @dev: Device requesting the SCU IPC device
> + *
> + * The recommended new API takes SCU IPC instance as parameter and this
> + * function can be called by driver to get the instance. This also makes
> + * sure the driver providing the IPC functionality cannot be unloaded
> + * while the caller has the instance.
> + *
> + * Returns %NULL if SCU IPC is not currently available.
> + */
> +struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
> +{
> +	struct intel_scu_ipc_devres *dr;
> +	struct intel_scu_ipc_dev *scu;
> +
> +	dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
> +	if (!dr)
> +		return NULL;
> +
> +	scu = intel_scu_ipc_dev_get();
> +	if (!scu) {
> +		devres_free(dr);
> +		return NULL;
> +	}
> +
> +	dr->scu = scu;
> +	devres_add(dev, dr);
> +
> +	return scu;
> +}
> +EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
> +
>  /*
>   * Send ipc command
>   * Command Register (Write Only):
> @@ -171,9 +268,9 @@ static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *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)
> +static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
> +			u32 count, u32 op, u32 id)
>  {
> -	struct intel_scu_ipc_dev *scu;
>  	int nc;
>  	u32 offset = 0;
>  	int err;
> @@ -183,11 +280,12 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
>  	memset(cbuf, 0, sizeof(cbuf));
>  
>  	mutex_lock(&ipclock);
> -	if (!ipcdev) {
> +	if (!scu)
> +		scu = ipcdev;
> +	if (!scu) {
>  		mutex_unlock(&ipclock);
>  		return -ENODEV;
>  	}
> -	scu = ipcdev;
>  
>  	for (nc = 0; nc < count; nc++, offset += 2) {
>  		cbuf[offset] = addr[nc];
> @@ -223,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
>  }
>  
>  /**
> - * intel_scu_ipc_ioread8		-	read a word via the SCU
> + * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
> + * @scu: Optional SCU IPC instance
>   * @addr: Register on SCU
>   * @data: Return pointer for read byte
>   *
> @@ -232,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
>   *
>   * This function may sleep.
>   */
> -int intel_scu_ipc_ioread8(u16 addr, u8 *data)
> +int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
>  {
> -	return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
> +	return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_ioread8);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
>  
>  /**
> - * intel_scu_ipc_iowrite8		-	write a byte via the SCU
> + * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
> + * @scu: Optional SCU IPC instance
>   * @addr: Register on SCU
>   * @data: Byte to write
>   *
> @@ -248,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
>   *
>   * This function may sleep.
>   */
> -int intel_scu_ipc_iowrite8(u16 addr, u8 data)
> +int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
>  {
> -	return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
> +	return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
>  
>  /**
> - * intel_scu_ipc_readvv		-	read a set of registers
> + * intel_scu_ipc_dev_readv() - Read a set of registers
> + * @scu: Optional SCU IPC instance
>   * @addr: Register list
>   * @data: Bytes to return
>   * @len: Length of array
> @@ -267,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
>   *
>   * This function may sleep.
>   */
> -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
> +int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
> +			    size_t len)
>  {
> -	return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
> +	return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_readv);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
>  
>  /**
> - * intel_scu_ipc_writev		-	write a set of registers
> + * intel_scu_ipc_dev_writev() - Write a set of registers
> + * @scu: Optional SCU IPC instance
>   * @addr: Register list
>   * @data: Bytes to write
>   * @len: Length of array
> @@ -286,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
>   *
>   * This function may sleep.
>   */
> -int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
> +int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
> +			     size_t len)
>  {
> -	return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
> +	return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_writev);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
>  
>  /**
> - * intel_scu_ipc_update_register	-	r/m/w a register
> + * intel_scu_ipc_dev_update() - Update a register
> + * @scu: Optional SCU IPC instance
>   * @addr: Register address
> - * @bits: Bits to update
> + * @data: Bits to update
>   * @mask: Mask of bits to update
>   *
>   * Read-modify-write power control unit register. The first data argument
> @@ -306,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
>   * This function may sleep. Locking between SCU accesses is handled
>   * for the caller.
>   */
> -int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
> +int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
> +			     u8 mask)
>  {
> -	u8 data[2] = { bits, mask };
> -	return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
> +	u8 tmp[2] = { data, mask };
> +	return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_update_register);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_update);
>  
>  /**
> - * intel_scu_ipc_simple_command	-	send a simple command
> + * intel_scu_ipc_dev_simple_command() - Send a simple command
> + * @scu: Optional SCU IPC instance
>   * @cmd: Command
>   * @sub: Sub type
>   *
> @@ -325,14 +432,16 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
>   * This function may sleep. Locking for SCU accesses is handled for the
>   * caller.
>   */
> -int intel_scu_ipc_simple_command(int cmd, int sub)
> +int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
> +				     int sub)
>  {
> -	struct intel_scu_ipc_dev *scu;
>  	u32 cmdval;
>  	int err;
>  
>  	mutex_lock(&ipclock);
> -	if (!ipcdev) {
> +	if (!scu)
> +		scu = ipcdev;
> +	if (!scu) {
>  		mutex_unlock(&ipclock);
>  		return -ENODEV;
>  	}
> @@ -345,44 +454,59 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
>  		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
>  	return err;
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_simple_command);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
>  
>  /**
> - * intel_scu_ipc_command	-	command with data
> + * intel_scu_ipc_command_with_size() - Command with data
> + * @scu: Optional SCU IPC instance
>   * @cmd: Command
>   * @sub: Sub type
>   * @in: Input data
> - * @inlen: Input length in dwords
> + * @inlen: Input length in bytes
> + * @size: Input size written to the IPC command register in whatever
> + *	  units (dword, byte) the particular firmware requires. Normally
> + *	  should be the same as @inlen.
>   * @out: Output data
> - * @outlen: Output length in dwords
> + * @outlen: Output length in bytes
>   *
>   * 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)
> +int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
> +					int sub, const void *in, size_t inlen,
> +					size_t size, void *out, size_t outlen)
>  {
> -	struct intel_scu_ipc_dev *scu;
> -	u32 cmdval;
> +	size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
> +	size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
> +	u32 cmdval, inbuf[4] = {};
>  	int i, err;
>  
> +	if (inbuflen > 4 || outbuflen > 4)
> +		return -EINVAL;
> +
>  	mutex_lock(&ipclock);
> -	if (!ipcdev) {
> +	if (!scu)
> +		scu = ipcdev;
> +	if (!scu) {
>  		mutex_unlock(&ipclock);
>  		return -ENODEV;
>  	}
> -	scu = ipcdev;
>  
> -	for (i = 0; i < inlen; i++)
> -		ipc_data_writel(scu, *in++, 4 * i);
> +	memcpy(inbuf, in, inlen);
> +	for (i = 0; i < inbuflen; i++)
> +		ipc_data_writel(scu, inbuf[i], 4 * i);
>  
> -	cmdval = (inlen << 16) | (sub << 12) | cmd;
> +	cmdval = (size << 16) | (sub << 12) | cmd;
>  	ipc_command(scu, cmdval);
>  	err = intel_scu_ipc_check_status(scu);
>  
>  	if (!err) {
> -		for (i = 0; i < outlen; i++)
> -			*out++ = ipc_data_readl(scu, 4 * i);
> +		u32 outbuf[4] = {};
> +
> +		for (i = 0; i < outbuflen; i++)
> +			outbuf[i] = ipc_data_readl(scu, 4 * i);
> +
> +		memcpy(out, outbuf, outlen);
>  	}
>  
>  	mutex_unlock(&ipclock);
> @@ -390,7 +514,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
>  		dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
>  	return err;
>  }
> -EXPORT_SYMBOL(intel_scu_ipc_command);
> +EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
>  
>  /*
>   * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
> @@ -423,17 +547,20 @@ static void intel_scu_ipc_release(struct device *dev)
>  }
>  
>  /**
> - * intel_scu_ipc_register() - Register SCU IPC device
> + * __intel_scu_ipc_register() - Register SCU IPC device
>   * @parent: Parent device
>   * @pdata: Platform specific data for SCU IPC
> + * @owner: Module registering the SCU IPC device
>   *
>   * Call this function to register SCU IPC mechanism under @parent.
>   * Returns pointer to the new SCU IPC device or ERR_PTR() in case of
> - * failure.
> + * failure. The caller may use the returned instance if it needs to do
> + * SCU IPC calls itself.
>   */
>  struct intel_scu_ipc_dev *
> -intel_scu_ipc_register(struct device *parent,
> -		       const struct intel_scu_ipc_pdata *pdata)
> +__intel_scu_ipc_register(struct device *parent,
> +			 const struct intel_scu_ipc_pdata *pdata,
> +			 struct module *owner)
>  {
>  	int err;
>  	struct intel_scu_ipc_dev *scu;
> @@ -452,6 +579,7 @@ intel_scu_ipc_register(struct device *parent,
>  		goto err_unlock;
>  	}
>  
> +	scu->owner = owner;
>  	scu->dev.parent = parent;
>  	scu->dev.class = &intel_scu_ipc_class;
>  	scu->dev.release = intel_scu_ipc_release;
> @@ -507,7 +635,7 @@ intel_scu_ipc_register(struct device *parent,
>  
>  	return ERR_PTR(err);
>  }
> -EXPORT_SYMBOL_GPL(intel_scu_ipc_register);
> +EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
>  
>  static int __init intel_scu_ipc_init(void)
>  {
> -- 
> 2.25.0
> 

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic
  2020-02-17 13:14 ` [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic Mika Westerberg
@ 2020-02-24 15:30   ` Lee Jones
  0 siblings, 0 replies; 30+ messages in thread
From: Lee Jones @ 2020-02-24 15:30 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Mon, 17 Feb 2020, Mika Westerberg wrote:

> Both PMIC drivers (intel_soc_pmic_mrfld and intel_soc_pmic_bxtwc) will
> be using this field going forward to access the SCU IPC instance.
> 
> While there add kernel-doc for the intel_soc_pmic structure.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  include/linux/mfd/intel_soc_pmic.h | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API
  2020-02-17 13:14 ` [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API Mika Westerberg
@ 2020-02-25  9:24   ` Lee Jones
  0 siblings, 0 replies; 30+ messages in thread
From: Lee Jones @ 2020-02-25  9:24 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Mon, 17 Feb 2020, Mika Westerberg wrote:

> Convert the Intel Broxton Whiskey Cover PMIC driver to use the new SCU
> IPC API. This allows us to get rid of the PMC IPC implementation which
> is now covered in SCU IPC driver. We drop the error log if the IPC
> command fails because intel_scu_ipc_dev_command() does that already.
> 
> Also move PMIC specific IPC message constants to the PMIC driver from
> the intel_pmc_ipc.h header.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  arch/x86/include/asm/intel_pmc_ipc.h |  3 ---
>  drivers/mfd/intel_soc_pmic_bxtwc.c   | 34 ++++++++++++++--------------
>  2 files changed, 17 insertions(+), 20 deletions(-)

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: Convert to use new SCU IPC API
  2020-02-17 13:14 ` [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: " Mika Westerberg
@ 2020-02-26  6:45   ` Lee Jones
  0 siblings, 0 replies; 30+ messages in thread
From: Lee Jones @ 2020-02-26  6:45 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Mon, 17 Feb 2020, Mika Westerberg wrote:

> This converts the Intel Merrifield PMIC driver over the new SCU IPC API
> where the SCU IPC instance is passed to the functions.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  drivers/mfd/intel_soc_pmic_mrfld.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)

For my own reference:
  Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD
  2020-02-17 13:14 ` [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD Mika Westerberg
@ 2020-02-26  8:47   ` Lee Jones
  2020-02-26 10:33     ` Mika Westerberg
  0 siblings, 1 reply; 30+ messages in thread
From: Lee Jones @ 2020-02-26  8:47 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Mon, 17 Feb 2020, Mika Westerberg wrote:

> This driver only creates a bunch of platform devices sharing resources
> belonging to the PMC device. This is pretty much what MFD subsystem is
> for so move the driver there, renaming it to intel_pmc_bxt.c which
> should be more clear what it is.
> 
> MFD subsystem provides nice helper APIs for subdevice creation so
> convert the driver to use those. Unfortunately the ACPI device includes
> separate resources for most of the subdevices so we cannot simply call
> mfd_add_devices() to create all of them but instead we need to call it
> separately for each device.
> 
> The new MFD driver continues to expose two sysfs attributes that allow
> userspace to send IPC commands to the PMC/SCU to avoid breaking any
> existing applications that may use these. Generally this is bad idea so
> document this in the ABI documentation.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
>  arch/x86/include/asm/intel_pmc_ipc.h          |  47 --
>  arch/x86/include/asm/intel_telemetry.h        |   1 +
>  drivers/mfd/Kconfig                           |  16 +-
>  drivers/mfd/Makefile                          |   1 +
>  drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++++++
>  drivers/platform/x86/Kconfig                  |  16 +-
>  drivers/platform/x86/Makefile                 |   1 -
>  drivers/platform/x86/intel_pmc_ipc.c          | 645 ------------------
>  .../platform/x86/intel_telemetry_debugfs.c    |  12 +-
>  drivers/platform/x86/intel_telemetry_pltdrv.c |   2 +
>  drivers/usb/typec/tcpm/Kconfig                |   2 +-
>  include/linux/mfd/intel_pmc_bxt.h             |  21 +
>  13 files changed, 565 insertions(+), 710 deletions(-)
>  create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
>  delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
>  create mode 100644 drivers/mfd/intel_pmc_bxt.c
>  delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
>  create mode 100644 include/linux/mfd/intel_pmc_bxt.h

[...]

> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 20b294ef2873..d41a965d819b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -551,7 +551,7 @@ config INTEL_SOC_PMIC
>  
>  config INTEL_SOC_PMIC_BXTWC
>  	tristate "Support for Intel Broxton Whiskey Cove PMIC"
> -	depends on INTEL_PMC_IPC
> +	depends on MFD_INTEL_PMC_BXT
>  	select MFD_CORE
>  	select REGMAP_IRQ
>  	help
> @@ -632,6 +632,20 @@ config MFD_INTEL_MSIC
>  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
>  	  devices used in Intel Medfield platforms.
>  
> +config MFD_INTEL_PMC_BXT
> +	tristate "Intel PMC Driver for Broxton"
> +	depends on X86
> +	depends on X86_PLATFORM_DEVICES
> +	depends on ACPI
> +	select INTEL_SCU_IPC
> +	select MFD_CORE
> +	help
> +	  This driver provides support for the PMC (Power Management
> +	  Controller) on Intel Broxton and Apollo Lake. The PMC is a
> +	  multi-function device that exposes IPC, General Control
> +	  Register and P-unit access. In addition this creates devices
> +	  for iTCO watchdog and telemetry that are part of the PMC.
> +
>  config MFD_IPAQ_MICRO
>  	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
>  	depends on SA1100_H3100 || SA1100_H3600
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index b83f172545e1..444264d42a20 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -212,6 +212,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
>  obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
>  obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
> +obj-$(CONFIG_MFD_INTEL_PMC_BXT)	+= intel_pmc_bxt.o
>  obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
>  obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
>  obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
> diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c
> new file mode 100644
> index 000000000000..7f743ae205de
> --- /dev/null
> +++ b/drivers/mfd/intel_pmc_bxt.c
> @@ -0,0 +1,489 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for the Intel Broxton PMC
> + *
> + * (C) Copyright 2014 - 2020 Intel Corporation
> + *
> + * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
> + * Sreedhara DS <sreedhara.ds@intel.com>
> + *
> + * The PMC running on the ARC processor communicates with another entity
> + * running in the IA core through an IPC mechanism which in turn sends
> + * messages between the IA and the PMC.

Please expand non-universal/non-obvious abbreviations in comments.

> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/intel_pmc_bxt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/itco_wdt.h>
> +
> +#include <asm/intel_scu_ipc.h>
> +
> +/* Residency with clock rate at 19.2MHz to usecs */
> +#define S0IX_RESIDENCY_IN_USECS(d, s)		\
> +({						\
> +	u64 result = 10ull * ((d) + (s));	\
> +	do_div(result, 192);			\
> +	result;					\
> +})
> +
> +/* Resources exported from IFWI */
> +#define PLAT_RESOURCE_IPC_INDEX		0
> +#define PLAT_RESOURCE_IPC_SIZE		0x1000
> +#define PLAT_RESOURCE_GCR_OFFSET	0x1000
> +#define PLAT_RESOURCE_GCR_SIZE		0x1000
> +#define PLAT_RESOURCE_BIOS_DATA_INDEX	1
> +#define PLAT_RESOURCE_BIOS_IFACE_INDEX	2
> +#define PLAT_RESOURCE_TELEM_SSRAM_INDEX	3
> +#define PLAT_RESOURCE_ISP_DATA_INDEX	4
> +#define PLAT_RESOURCE_ISP_IFACE_INDEX	5
> +#define PLAT_RESOURCE_GTD_DATA_INDEX	6
> +#define PLAT_RESOURCE_GTD_IFACE_INDEX	7
> +#define PLAT_RESOURCE_ACPI_IO_INDEX	0
> +
> +/*
> + * BIOS does not create an ACPI device for each PMC function, but
> + * exports multiple resources from one ACPI device (IPC) for multiple
> + * functions. This driver is responsible for creating a child device and
> + * to export resources for those functions.
> + */
> +#define TCO_DEVICE_NAME			"iTCO_wdt"

This is nearly always horrible.

Please just use the string in it's place.

> +#define SMI_EN_OFFSET			0x40
> +#define SMI_EN_SIZE			4
> +#define TCO_BASE_OFFSET			0x60
> +#define TCO_REGS_SIZE			16

> +#define PUNIT_DEVICE_NAME		"intel_punit_ipc"
> +#define TELEMETRY_DEVICE_NAME		"intel_telemetry"

As above.

> +#define TELEM_SSRAM_SIZE		240
> +#define TELEM_PMC_SSRAM_OFFSET		0x1B00
> +#define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
> +
> +/* Commands */
> +#define PMC_NORTHPEAK_CTRL		0xED
> +
> +/* PMC_CFG_REG bit masks */
> +#define PMC_CFG_NO_REBOOT_EN		BIT(4)
> +
> +/* Index to cells array in below struct */
> +enum {
> +	PMC_TCO,
> +	PMC_PUNIT,
> +	PMC_TELEM,
> +};
> +
> +struct intel_pmc_dev {
> +	struct device *dev;
> +	struct intel_scu_ipc_dev *scu;
> +
> +	struct mfd_cell cells[PMC_TELEM + 1];

Nicer to add a "PMC_DEVICE_MAX" enum and use that.

Why do these even need to be in here?

I would normally suggest creating a cell per device.

> +	void __iomem *gcr_mem_base;
> +	spinlock_t gcr_lock;
> +
> +	struct resource *telem_base;
> +};
> +
> +static inline bool is_gcr_valid(u32 offset)
> +{
> +	return offset < PLAT_RESOURCE_GCR_SIZE - 8;
> +}
> +
> +/**
> + * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
> + * @pmc: PMC device pointer
> + * @offset: offset of GCR register from GCR address base
> + * @data: data pointer for storing the register output
> + *
> + * Reads the 64-bit PMC GCR register at given offset.
> + *
> + * Return: Negative value on error or 0 on success.
> + */
> +int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
> +{
> +	if (!is_gcr_valid(offset))
> +		return -EINVAL;
> +
> +	spin_lock(&pmc->gcr_lock);
> +	*data = readq(pmc->gcr_mem_base + offset);
> +	spin_unlock(&pmc->gcr_lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
> +
> +/**
> + * intel_pmc_gcr_update() - Update PMC GCR register bits
> + * @pmc: PMC device pointer
> + * @offset: offset of GCR register from GCR address base
> + * @mask: bit mask for update operation
> + * @val: update value
> + *
> + * Updates the bits of given GCR register as specified by
> + * @mask and @val.
> + *
> + * Return: Negative value on error or 0 on success.
> + */
> +static int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask,
> +				u32 val)
> +{
> +	u32 new_val;
> +
> +	if (!is_gcr_valid(offset))
> +		return -EINVAL;
> +
> +	spin_lock(&pmc->gcr_lock);
> +	new_val = readl(pmc->gcr_mem_base + offset);
> +
> +	new_val = (new_val & ~mask) | (val & mask);
> +	writel(new_val, pmc->gcr_mem_base + offset);
> +
> +	new_val = readl(pmc->gcr_mem_base + offset);
> +	spin_unlock(&pmc->gcr_lock);
> +
> +	/* Check whether the bit update is successful */
> +	return (new_val & mask) != (val & mask) ? -EIO : 0;
> +}
> +
> +/**
> + * intel_pmc_s0ix_counter_read() - Read S0ix residency.
> + * @pmc: PMC device pointer
> + * @data: Out param that contains current S0ix residency count.
> + *
> + * Writes to @data how many usecs the system has been in low-power S0ix
> + * state.
> + *
> + * Return: An error code or 0 on success.
> + */
> +int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
> +{
> +	u64 deep, shlw;
> +
> +	spin_lock(&pmc->gcr_lock);
> +	deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
> +	shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
> +	spin_unlock(&pmc->gcr_lock);
> +
> +	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
> +
> +static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,

Header with explanation please.

> +			       const char *buf, size_t count)
> +{
> +	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
> +	struct intel_scu_ipc_dev *scu = pmc->scu;
> +	int subcmd;
> +	int cmd;
> +	int ret;
> +
> +	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
> +	if (ret != 2) {
> +		dev_err(dev, "Invalid values, expected: cmd subcmd\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
> +	return ret ?: count;

Prefer the usual "if (ret) return ret;" over ternary.

> +}
> +static DEVICE_ATTR_WO(simplecmd);
> +
> +static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
> +			       const char *buf, size_t count)

Header with explanation please.

> +{
> +	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
> +	struct intel_scu_ipc_dev *scu = pmc->scu;
> +	unsigned long val;
> +	int subcmd;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val)
> +		subcmd = 1;
> +	else
> +		subcmd = 0;

Comment please.

> +	ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
> +	return ret ?: count;

As above.

> +}
> +static DEVICE_ATTR_WO(northpeak);
> +
> +static struct attribute *intel_pmc_attrs[] = {
> +	&dev_attr_northpeak.attr,
> +	&dev_attr_simplecmd.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group intel_pmc_group = {
> +	.attrs = intel_pmc_attrs,
> +};
> +
> +static const struct attribute_group *intel_pmc_groups[] = {
> +	&intel_pmc_group,
> +	NULL
> +};
> +
> +/* Templates used to construct MFD cells */
> +
> +static const struct mfd_cell punit = {
> +	.name = PUNIT_DEVICE_NAME,

Use proper string please.

> +};
> +
> +static int update_no_reboot_bit(void *priv, bool set)
> +{
> +	struct intel_pmc_dev *pmc = priv;
> +	u32 bits = PMC_CFG_NO_REBOOT_EN;
> +	u32 value = set ? bits : 0;
> +
> +	return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
> +}
> +
> +static const struct itco_wdt_platform_data tco_pdata = {
> +	.name = "Apollo Lake SoC",
> +	.version = 5,
> +	.update_no_reboot_bit = update_no_reboot_bit,
> +};
> +
> +static const struct mfd_cell tco = {
> +	.name = TCO_DEVICE_NAME,

Use proper string please.

> +	.ignore_resource_conflicts = true,

Why not add tco_pdata here?

> +};
> +
> +static const struct resource telem_res[] = {
> +	DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> +	DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> +};
> +
> +static const struct mfd_cell telem = {
> +	.name = TELEMETRY_DEVICE_NAME,

Use proper string please.

> +	.resources = telem_res,
> +	.num_resources = ARRAY_SIZE(telem_res),
> +};
> +
> +static int intel_pmc_get_tco_resources(struct platform_device *pdev,
> +				       struct intel_pmc_dev *pmc)
> +{
> +	struct itco_wdt_platform_data *pdata;
> +	struct resource *res, *tco_res;
> +
> +	if (acpi_has_watchdog())
> +		return 0;
> +
> +	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 -EINVAL;
> +	}
> +
> +	tco_res = devm_kcalloc(&pdev->dev, 2, sizeof(*tco_res), GFP_KERNEL);
> +	if (!tco_res)
> +		return -ENOMEM;
> +
> +	tco_res[0].flags = IORESOURCE_IO;
> +	tco_res[0].start = res->start + TCO_BASE_OFFSET;
> +	tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
> +	tco_res[1].flags = IORESOURCE_IO;
> +	tco_res[1].start = res->start + SMI_EN_OFFSET;
> +	tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
> +
> +	pmc->cells[PMC_TCO].resources = tco_res;
> +	pmc->cells[PMC_TCO].num_resources = 2;
> +
> +	pdata = devm_kmemdup(&pdev->dev, &tco_pdata, sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return -ENOMEM;
> +
> +	pdata->no_reboot_priv = pmc;

This looks hacky.  What are you doing here?

> +	pmc->cells[PMC_TCO].platform_data = pdata;
> +	pmc->cells[PMC_TCO].pdata_size = sizeof(*pdata);
> +
> +	return 0;
> +}
> +
> +static int intel_pmc_get_resources(struct platform_device *pdev,
> +				   struct intel_pmc_dev *pmc,
> +				   struct intel_scu_ipc_pdata *pdata)
> +{
> +	struct resource *res, *punit_res;
> +	struct resource gcr_res;
> +	size_t npunit_res = 0;
> +	int ret;
> +
> +	pdata->irq = platform_get_irq_optional(pdev, 0);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_IPC_INDEX);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get IPC resource\n");
> +		return -EINVAL;
> +	}
> +
> +	/* IPC registers */
> +	pdata->mem.flags = res->flags;
> +	pdata->mem.start = res->start;
> +	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;

Passing register addresses through pdata also looks like a hack.

Why not pass via resources?

> +	/* GCR registers */
> +	gcr_res.flags = res->flags;
> +	gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
> +	gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
> +
> +	pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
> +	if (IS_ERR(pmc->gcr_mem_base))
> +		return PTR_ERR(pmc->gcr_mem_base);
> +
> +	pmc->cells[PMC_TCO] = tco;
> +	pmc->cells[PMC_PUNIT] = punit;
> +	pmc->cells[PMC_TELEM] = telem;
> +
> +	/* iTCO watchdog only if there is no WDAT ACPI table */

This sentence doesn't make sense.

"Only register ... " ?

> +	ret = intel_pmc_get_tco_resources(pdev, pmc);
> +	if (ret)
> +		return ret;
> +
> +	punit_res = devm_kcalloc(&pdev->dev, 6, sizeof(*punit_res), GFP_KERNEL);
> +	if (!punit_res)
> +		return -ENOMEM;
> +
> +	/* This is index 0 to cover BIOS data register */

We don't need to know what the indexes are.

Just leave it at "BIOS data register".

> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_BIOS_DATA_INDEX);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n");
> +		return -EINVAL;
> +	}
> +	punit_res[npunit_res++] = *res;
> +
> +	/* This is index 1 to cover BIOS interface register */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
> +		return -EINVAL;
> +	}
> +	punit_res[npunit_res++] = *res;
> +
> +	/* This is index 2 to cover ISP data register, optional */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_ISP_DATA_INDEX);
> +	if (res)
> +		punit_res[npunit_res++] = *res;
> +
> +	/* This is index 3 to cover ISP interface register, optional */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_ISP_IFACE_INDEX);
> +	if (res)
> +		punit_res[npunit_res++] = *res;
> +
> +	/* This is index 4 to cover GTD data register, optional */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_GTD_DATA_INDEX);
> +	if (res)
> +		punit_res[npunit_res++] = *res;
> +
> +	/* This is index 5 to cover GTD interface register, optional */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_GTD_IFACE_INDEX);
> +	if (res)
> +		punit_res[npunit_res++] = *res;
> +
> +	pmc->cells[PMC_PUNIT].resources = punit_res;
> +	pmc->cells[PMC_PUNIT].num_resources = npunit_res;
> +
> +	/* Telemetry SSRAM is optional */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> +				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
> +	if (res)
> +		pmc->telem_base = res;
> +
> +	return 0;
> +}
> +
> +static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
> +{
> +	int ret;
> +
> +	if (pmc->cells[PMC_TCO].num_resources) {

Why not use the same (only?) condition that could make this false:

  if (acpi_has_watchdog())

> +		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> +					   &pmc->cells[PMC_TCO], 1, NULL, 0, NULL);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> +				   &pmc->cells[PMC_PUNIT], 1, NULL, 0, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (pmc->telem_base) {
> +		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> +					   &pmc->cells[PMC_TELEM], 1,
> +					   pmc->telem_base, 0, NULL);
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct acpi_device_id intel_pmc_acpi_ids[] = {
> +	{ "INT34D2" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
> +
> +static int intel_pmc_probe(struct platform_device *pdev)
> +{
> +	struct intel_scu_ipc_pdata pdata = {};
> +	struct intel_pmc_dev *pmc;
> +	int ret;
> +
> +	pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
> +	if (!pmc)
> +		return -ENOMEM;
> +
> +	pmc->dev = &pdev->dev;
> +	spin_lock_init(&pmc->gcr_lock);
> +
> +	ret = intel_pmc_get_resources(pdev, pmc, &pdata);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request resources\n");
> +		return ret;
> +	}
> +
> +	pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
> +	if (IS_ERR(pmc->scu))
> +		return PTR_ERR(pmc->scu);
> +
> +	platform_set_drvdata(pdev, pmc);
> +
> +	ret = intel_pmc_create_devices(pmc);
> +	if (ret)
> +		dev_err(&pdev->dev, "Failed to create PMC devices\n");
> +
> +	return ret;
> +}
> +
> +static struct platform_driver intel_pmc_driver = {
> +	.probe = intel_pmc_probe,
> +	.driver = {
> +		.name = "intel_pmc_bxt",
> +		.acpi_match_table = intel_pmc_acpi_ids,
> +		.dev_groups = intel_pmc_groups,
> +	},
> +};
> +

Remove this line.

> +module_platform_driver(intel_pmc_driver);
> +
> +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
> +MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
> +MODULE_DESCRIPTION("Intel Broxton PMC driver");
> +MODULE_LICENSE("GPL v2");

[...]

> diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
> new file mode 100644
> index 000000000000..a5fb41910d78
> --- /dev/null
> +++ b/include/linux/mfd/intel_pmc_bxt.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef MFD_INTEL_PMC_BXT_H
> +#define MFD_INTEL_PMC_BXT_H
> +
> +#include <linux/types.h>
> +
> +/* GCR reg offsets from GCR base */
> +#define PMC_GCR_PMC_CFG_REG		0x08
> +#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
> +#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> +
> +/*
> + * Pointer to the PMC device can be obtained by calling
> + * dev_get_drvdata() to the parent MFD device.
> + */
> +struct intel_pmc_dev;

Don't you have a shared header file you can put the definition in
instead?

> +int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
> +int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
> +
> +#endif /* MFD_INTEL_PMC_BXT_H */

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD
  2020-02-26  8:47   ` Lee Jones
@ 2020-02-26 10:33     ` Mika Westerberg
  2020-02-26 11:23       ` Lee Jones
  0 siblings, 1 reply; 30+ messages in thread
From: Mika Westerberg @ 2020-02-26 10:33 UTC (permalink / raw)
  To: Lee Jones
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Wed, Feb 26, 2020 at 08:47:49AM +0000, Lee Jones wrote:
> On Mon, 17 Feb 2020, Mika Westerberg wrote:
> 
> > This driver only creates a bunch of platform devices sharing resources
> > belonging to the PMC device. This is pretty much what MFD subsystem is
> > for so move the driver there, renaming it to intel_pmc_bxt.c which
> > should be more clear what it is.
> > 
> > MFD subsystem provides nice helper APIs for subdevice creation so
> > convert the driver to use those. Unfortunately the ACPI device includes
> > separate resources for most of the subdevices so we cannot simply call
> > mfd_add_devices() to create all of them but instead we need to call it
> > separately for each device.
> > 
> > The new MFD driver continues to expose two sysfs attributes that allow
> > userspace to send IPC commands to the PMC/SCU to avoid breaking any
> > existing applications that may use these. Generally this is bad idea so
> > document this in the ABI documentation.
> > 
> > Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> > Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > ---
> >  .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
> >  arch/x86/include/asm/intel_pmc_ipc.h          |  47 --
> >  arch/x86/include/asm/intel_telemetry.h        |   1 +
> >  drivers/mfd/Kconfig                           |  16 +-
> >  drivers/mfd/Makefile                          |   1 +
> >  drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++++++
> >  drivers/platform/x86/Kconfig                  |  16 +-
> >  drivers/platform/x86/Makefile                 |   1 -
> >  drivers/platform/x86/intel_pmc_ipc.c          | 645 ------------------
> >  .../platform/x86/intel_telemetry_debugfs.c    |  12 +-
> >  drivers/platform/x86/intel_telemetry_pltdrv.c |   2 +
> >  drivers/usb/typec/tcpm/Kconfig                |   2 +-
> >  include/linux/mfd/intel_pmc_bxt.h             |  21 +
> >  13 files changed, 565 insertions(+), 710 deletions(-)
> >  create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
> >  delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
> >  create mode 100644 drivers/mfd/intel_pmc_bxt.c
> >  delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
> >  create mode 100644 include/linux/mfd/intel_pmc_bxt.h
> 
> [...]
> 
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 20b294ef2873..d41a965d819b 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -551,7 +551,7 @@ config INTEL_SOC_PMIC
> >  
> >  config INTEL_SOC_PMIC_BXTWC
> >  	tristate "Support for Intel Broxton Whiskey Cove PMIC"
> > -	depends on INTEL_PMC_IPC
> > +	depends on MFD_INTEL_PMC_BXT
> >  	select MFD_CORE
> >  	select REGMAP_IRQ
> >  	help
> > @@ -632,6 +632,20 @@ config MFD_INTEL_MSIC
> >  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
> >  	  devices used in Intel Medfield platforms.
> >  
> > +config MFD_INTEL_PMC_BXT
> > +	tristate "Intel PMC Driver for Broxton"
> > +	depends on X86
> > +	depends on X86_PLATFORM_DEVICES
> > +	depends on ACPI
> > +	select INTEL_SCU_IPC
> > +	select MFD_CORE
> > +	help
> > +	  This driver provides support for the PMC (Power Management
> > +	  Controller) on Intel Broxton and Apollo Lake. The PMC is a
> > +	  multi-function device that exposes IPC, General Control
> > +	  Register and P-unit access. In addition this creates devices
> > +	  for iTCO watchdog and telemetry that are part of the PMC.
> > +
> >  config MFD_IPAQ_MICRO
> >  	bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
> >  	depends on SA1100_H3100 || SA1100_H3600
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index b83f172545e1..444264d42a20 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -212,6 +212,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)	+= intel-lpss.o
> >  obj-$(CONFIG_MFD_INTEL_LPSS_PCI)	+= intel-lpss-pci.o
> >  obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)	+= intel-lpss-acpi.o
> >  obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
> > +obj-$(CONFIG_MFD_INTEL_PMC_BXT)	+= intel_pmc_bxt.o
> >  obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
> >  obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
> >  obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o
> > diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c
> > new file mode 100644
> > index 000000000000..7f743ae205de
> > --- /dev/null
> > +++ b/drivers/mfd/intel_pmc_bxt.c
> > @@ -0,0 +1,489 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for the Intel Broxton PMC
> > + *
> > + * (C) Copyright 2014 - 2020 Intel Corporation
> > + *
> > + * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
> > + * Sreedhara DS <sreedhara.ds@intel.com>
> > + *
> > + * The PMC running on the ARC processor communicates with another entity
> > + * running in the IA core through an IPC mechanism which in turn sends
> > + * messages between the IA and the PMC.
> 
> Please expand non-universal/non-obvious abbreviations in comments.

Okay.

> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io-64-nonatomic-lo-hi.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/intel_pmc_bxt.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/platform_data/itco_wdt.h>
> > +
> > +#include <asm/intel_scu_ipc.h>
> > +
> > +/* Residency with clock rate at 19.2MHz to usecs */
> > +#define S0IX_RESIDENCY_IN_USECS(d, s)		\
> > +({						\
> > +	u64 result = 10ull * ((d) + (s));	\
> > +	do_div(result, 192);			\
> > +	result;					\
> > +})
> > +
> > +/* Resources exported from IFWI */
> > +#define PLAT_RESOURCE_IPC_INDEX		0
> > +#define PLAT_RESOURCE_IPC_SIZE		0x1000
> > +#define PLAT_RESOURCE_GCR_OFFSET	0x1000
> > +#define PLAT_RESOURCE_GCR_SIZE		0x1000
> > +#define PLAT_RESOURCE_BIOS_DATA_INDEX	1
> > +#define PLAT_RESOURCE_BIOS_IFACE_INDEX	2
> > +#define PLAT_RESOURCE_TELEM_SSRAM_INDEX	3
> > +#define PLAT_RESOURCE_ISP_DATA_INDEX	4
> > +#define PLAT_RESOURCE_ISP_IFACE_INDEX	5
> > +#define PLAT_RESOURCE_GTD_DATA_INDEX	6
> > +#define PLAT_RESOURCE_GTD_IFACE_INDEX	7
> > +#define PLAT_RESOURCE_ACPI_IO_INDEX	0
> > +
> > +/*
> > + * BIOS does not create an ACPI device for each PMC function, but
> > + * exports multiple resources from one ACPI device (IPC) for multiple
> > + * functions. This driver is responsible for creating a child device and
> > + * to export resources for those functions.
> > + */
> > +#define TCO_DEVICE_NAME			"iTCO_wdt"
> 
> This is nearly always horrible.
> 
> Please just use the string in it's place.
> 
> > +#define SMI_EN_OFFSET			0x40
> > +#define SMI_EN_SIZE			4
> > +#define TCO_BASE_OFFSET			0x60
> > +#define TCO_REGS_SIZE			16
> 
> > +#define PUNIT_DEVICE_NAME		"intel_punit_ipc"
> > +#define TELEMETRY_DEVICE_NAME		"intel_telemetry"
> 
> As above.

Yup.

> > +#define TELEM_SSRAM_SIZE		240
> > +#define TELEM_PMC_SSRAM_OFFSET		0x1B00
> > +#define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
> > +
> > +/* Commands */
> > +#define PMC_NORTHPEAK_CTRL		0xED
> > +
> > +/* PMC_CFG_REG bit masks */
> > +#define PMC_CFG_NO_REBOOT_EN		BIT(4)
> > +
> > +/* Index to cells array in below struct */
> > +enum {
> > +	PMC_TCO,
> > +	PMC_PUNIT,
> > +	PMC_TELEM,
> > +};
> > +
> > +struct intel_pmc_dev {
> > +	struct device *dev;
> > +	struct intel_scu_ipc_dev *scu;
> > +
> > +	struct mfd_cell cells[PMC_TELEM + 1];
> 
> Nicer to add a "PMC_DEVICE_MAX" enum and use that.
> 
> Why do these even need to be in here?

They need to be here because we need to fill them in dynamically based
on the resources we get from the ACPI device.

> I would normally suggest creating a cell per device.

You mean 

struct intel_pmc_dev {
	...
	struct mfd_cell tco_cell;
	struct mfd_cell punit_cell;
	...

right? Sure no problem.

> > +	void __iomem *gcr_mem_base;
> > +	spinlock_t gcr_lock;
> > +
> > +	struct resource *telem_base;
> > +};
> > +
> > +static inline bool is_gcr_valid(u32 offset)
> > +{
> > +	return offset < PLAT_RESOURCE_GCR_SIZE - 8;
> > +}
> > +
> > +/**
> > + * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
> > + * @pmc: PMC device pointer
> > + * @offset: offset of GCR register from GCR address base
> > + * @data: data pointer for storing the register output
> > + *
> > + * Reads the 64-bit PMC GCR register at given offset.
> > + *
> > + * Return: Negative value on error or 0 on success.
> > + */
> > +int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
> > +{
> > +	if (!is_gcr_valid(offset))
> > +		return -EINVAL;
> > +
> > +	spin_lock(&pmc->gcr_lock);
> > +	*data = readq(pmc->gcr_mem_base + offset);
> > +	spin_unlock(&pmc->gcr_lock);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
> > +
> > +/**
> > + * intel_pmc_gcr_update() - Update PMC GCR register bits
> > + * @pmc: PMC device pointer
> > + * @offset: offset of GCR register from GCR address base
> > + * @mask: bit mask for update operation
> > + * @val: update value
> > + *
> > + * Updates the bits of given GCR register as specified by
> > + * @mask and @val.
> > + *
> > + * Return: Negative value on error or 0 on success.
> > + */
> > +static int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask,
> > +				u32 val)
> > +{
> > +	u32 new_val;
> > +
> > +	if (!is_gcr_valid(offset))
> > +		return -EINVAL;
> > +
> > +	spin_lock(&pmc->gcr_lock);
> > +	new_val = readl(pmc->gcr_mem_base + offset);
> > +
> > +	new_val = (new_val & ~mask) | (val & mask);
> > +	writel(new_val, pmc->gcr_mem_base + offset);
> > +
> > +	new_val = readl(pmc->gcr_mem_base + offset);
> > +	spin_unlock(&pmc->gcr_lock);
> > +
> > +	/* Check whether the bit update is successful */
> > +	return (new_val & mask) != (val & mask) ? -EIO : 0;
> > +}
> > +
> > +/**
> > + * intel_pmc_s0ix_counter_read() - Read S0ix residency.
> > + * @pmc: PMC device pointer
> > + * @data: Out param that contains current S0ix residency count.
> > + *
> > + * Writes to @data how many usecs the system has been in low-power S0ix
> > + * state.
> > + *
> > + * Return: An error code or 0 on success.
> > + */
> > +int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
> > +{
> > +	u64 deep, shlw;
> > +
> > +	spin_lock(&pmc->gcr_lock);
> > +	deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
> > +	shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
> > +	spin_unlock(&pmc->gcr_lock);
> > +
> > +	*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
> > +
> > +static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,
> 
> Header with explanation please.

kernel-doc, right? I'll do that.

> > +			       const char *buf, size_t count)
> > +{
> > +	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
> > +	struct intel_scu_ipc_dev *scu = pmc->scu;
> > +	int subcmd;
> > +	int cmd;
> > +	int ret;
> > +
> > +	ret = sscanf(buf, "%d %d", &cmd, &subcmd);
> > +	if (ret != 2) {
> > +		dev_err(dev, "Invalid values, expected: cmd subcmd\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
> > +	return ret ?: count;
> 
> Prefer the usual "if (ret) return ret;" over ternary.

Sure.

> > +}
> > +static DEVICE_ATTR_WO(simplecmd);
> > +
> > +static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
> > +			       const char *buf, size_t count)
> 
> Header with explanation please.

Yup.

> > +{
> > +	struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
> > +	struct intel_scu_ipc_dev *scu = pmc->scu;
> > +	unsigned long val;
> > +	int subcmd;
> > +	int ret;
> > +
> > +	ret = kstrtoul(buf, 0, &val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (val)
> > +		subcmd = 1;
> > +	else
> > +		subcmd = 0;
> 
> Comment please.

OK.

> 
> > +	ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
> > +	return ret ?: count;
> 
> As above.

OK.

> > +}
> > +static DEVICE_ATTR_WO(northpeak);
> > +
> > +static struct attribute *intel_pmc_attrs[] = {
> > +	&dev_attr_northpeak.attr,
> > +	&dev_attr_simplecmd.attr,
> > +	NULL
> > +};
> > +
> > +static const struct attribute_group intel_pmc_group = {
> > +	.attrs = intel_pmc_attrs,
> > +};
> > +
> > +static const struct attribute_group *intel_pmc_groups[] = {
> > +	&intel_pmc_group,
> > +	NULL
> > +};
> > +
> > +/* Templates used to construct MFD cells */
> > +
> > +static const struct mfd_cell punit = {
> > +	.name = PUNIT_DEVICE_NAME,
> 
> Use proper string please.

Sure.

> > +};
> > +
> > +static int update_no_reboot_bit(void *priv, bool set)
> > +{
> > +	struct intel_pmc_dev *pmc = priv;
> > +	u32 bits = PMC_CFG_NO_REBOOT_EN;
> > +	u32 value = set ? bits : 0;
> > +
> > +	return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
> > +}
> > +
> > +static const struct itco_wdt_platform_data tco_pdata = {
> > +	.name = "Apollo Lake SoC",
> > +	.version = 5,
> > +	.update_no_reboot_bit = update_no_reboot_bit,
> > +};
> > +
> > +static const struct mfd_cell tco = {
> > +	.name = TCO_DEVICE_NAME,
> 
> Use proper string please.
> 
> > +	.ignore_resource_conflicts = true,
> 
> Why not add tco_pdata here?

Because we need to pass it the private PMC pointer that is filled later
on. It is being used by the iTCO_wdt .update_no_reboot_bit() callback as
its private data.

> > +};
> > +
> > +static const struct resource telem_res[] = {
> > +	DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > +	DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > +};
> > +
> > +static const struct mfd_cell telem = {
> > +	.name = TELEMETRY_DEVICE_NAME,
> 
> Use proper string please.

Okay.

> > +	.resources = telem_res,
> > +	.num_resources = ARRAY_SIZE(telem_res),
> > +};
> > +
> > +static int intel_pmc_get_tco_resources(struct platform_device *pdev,
> > +				       struct intel_pmc_dev *pmc)
> > +{
> > +	struct itco_wdt_platform_data *pdata;
> > +	struct resource *res, *tco_res;
> > +
> > +	if (acpi_has_watchdog())
> > +		return 0;
> > +
> > +	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 -EINVAL;
> > +	}
> > +
> > +	tco_res = devm_kcalloc(&pdev->dev, 2, sizeof(*tco_res), GFP_KERNEL);
> > +	if (!tco_res)
> > +		return -ENOMEM;
> > +
> > +	tco_res[0].flags = IORESOURCE_IO;
> > +	tco_res[0].start = res->start + TCO_BASE_OFFSET;
> > +	tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
> > +	tco_res[1].flags = IORESOURCE_IO;
> > +	tco_res[1].start = res->start + SMI_EN_OFFSET;
> > +	tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
> > +
> > +	pmc->cells[PMC_TCO].resources = tco_res;
> > +	pmc->cells[PMC_TCO].num_resources = 2;
> > +
> > +	pdata = devm_kmemdup(&pdev->dev, &tco_pdata, sizeof(*pdata), GFP_KERNEL);
> > +	if (!pdata)
> > +		return -ENOMEM;
> > +
> > +	pdata->no_reboot_priv = pmc;
> 
> This looks hacky.  What are you doing here?

So the pmc instance is created per device as you requested. This one
passes it to the iTCO_wdt .update_no_reboot_bit() callback which we
implemented in this driver (sane name update_no_reboot_bit()).

The iTCO_wdt platform data can be found in this header if you want to
take a look: include/linux/platform_data/itco_wdt.h.

> > +	pmc->cells[PMC_TCO].platform_data = pdata;
> > +	pmc->cells[PMC_TCO].pdata_size = sizeof(*pdata);
> > +
> > +	return 0;
> > +}
> > +
> > +static int intel_pmc_get_resources(struct platform_device *pdev,
> > +				   struct intel_pmc_dev *pmc,
> > +				   struct intel_scu_ipc_pdata *pdata)
> > +{
> > +	struct resource *res, *punit_res;
> > +	struct resource gcr_res;
> > +	size_t npunit_res = 0;
> > +	int ret;
> > +
> > +	pdata->irq = platform_get_irq_optional(pdev, 0);
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_IPC_INDEX);
> > +	if (!res) {
> > +		dev_err(&pdev->dev, "Failed to get IPC resource\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* IPC registers */
> > +	pdata->mem.flags = res->flags;
> > +	pdata->mem.start = res->start;
> > +	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
> 
> Passing register addresses through pdata also looks like a hack.
> 
> Why not pass via resources?

It is not actual "platform data" but just a structure that we pass for
the IPC registration function that then creates the underlying device
for the SCU IPC using these. Since it is plain device (not struct
platform device) it does not have the concept of "resources" such as
platform devices have.

> > +	/* GCR registers */
> > +	gcr_res.flags = res->flags;
> > +	gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
> > +	gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
> > +
> > +	pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
> > +	if (IS_ERR(pmc->gcr_mem_base))
> > +		return PTR_ERR(pmc->gcr_mem_base);
> > +
> > +	pmc->cells[PMC_TCO] = tco;
> > +	pmc->cells[PMC_PUNIT] = punit;
> > +	pmc->cells[PMC_TELEM] = telem;
> > +
> > +	/* iTCO watchdog only if there is no WDAT ACPI table */
> 
> This sentence doesn't make sense.
> 
> "Only register ... " ?

Heh, good point. I'll fix.

> > +	ret = intel_pmc_get_tco_resources(pdev, pmc);
> > +	if (ret)
> > +		return ret;
> > +
> > +	punit_res = devm_kcalloc(&pdev->dev, 6, sizeof(*punit_res), GFP_KERNEL);
> > +	if (!punit_res)
> > +		return -ENOMEM;
> > +
> > +	/* This is index 0 to cover BIOS data register */
> 
> We don't need to know what the indexes are.
> 
> Just leave it at "BIOS data register".

Okay.

> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_BIOS_DATA_INDEX);
> > +	if (!res) {
> > +		dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n");
> > +		return -EINVAL;
> > +	}
> > +	punit_res[npunit_res++] = *res;
> > +
> > +	/* This is index 1 to cover BIOS interface register */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_BIOS_IFACE_INDEX);
> > +	if (!res) {
> > +		dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
> > +		return -EINVAL;
> > +	}
> > +	punit_res[npunit_res++] = *res;
> > +
> > +	/* This is index 2 to cover ISP data register, optional */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_ISP_DATA_INDEX);
> > +	if (res)
> > +		punit_res[npunit_res++] = *res;
> > +
> > +	/* This is index 3 to cover ISP interface register, optional */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_ISP_IFACE_INDEX);
> > +	if (res)
> > +		punit_res[npunit_res++] = *res;
> > +
> > +	/* This is index 4 to cover GTD data register, optional */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_GTD_DATA_INDEX);
> > +	if (res)
> > +		punit_res[npunit_res++] = *res;
> > +
> > +	/* This is index 5 to cover GTD interface register, optional */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_GTD_IFACE_INDEX);
> > +	if (res)
> > +		punit_res[npunit_res++] = *res;
> > +
> > +	pmc->cells[PMC_PUNIT].resources = punit_res;
> > +	pmc->cells[PMC_PUNIT].num_resources = npunit_res;
> > +
> > +	/* Telemetry SSRAM is optional */
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > +				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
> > +	if (res)
> > +		pmc->telem_base = res;
> > +
> > +	return 0;
> > +}
> > +
> > +static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
> > +{
> > +	int ret;
> > +
> > +	if (pmc->cells[PMC_TCO].num_resources) {
> 
> Why not use the same (only?) condition that could make this false:
> 
>   if (acpi_has_watchdog())

Right, good point. I'll use that instead.

> > +		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> > +					   &pmc->cells[PMC_TCO], 1, NULL, 0, NULL);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> > +				   &pmc->cells[PMC_PUNIT], 1, NULL, 0, NULL);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (pmc->telem_base) {
> > +		ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
> > +					   &pmc->cells[PMC_TELEM], 1,
> > +					   pmc->telem_base, 0, NULL);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct acpi_device_id intel_pmc_acpi_ids[] = {
> > +	{ "INT34D2" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
> > +
> > +static int intel_pmc_probe(struct platform_device *pdev)
> > +{
> > +	struct intel_scu_ipc_pdata pdata = {};
> > +	struct intel_pmc_dev *pmc;
> > +	int ret;
> > +
> > +	pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
> > +	if (!pmc)
> > +		return -ENOMEM;
> > +
> > +	pmc->dev = &pdev->dev;
> > +	spin_lock_init(&pmc->gcr_lock);
> > +
> > +	ret = intel_pmc_get_resources(pdev, pmc, &pdata);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "Failed to request resources\n");
> > +		return ret;
> > +	}
> > +
> > +	pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &pdata);
> > +	if (IS_ERR(pmc->scu))
> > +		return PTR_ERR(pmc->scu);
> > +
> > +	platform_set_drvdata(pdev, pmc);
> > +
> > +	ret = intel_pmc_create_devices(pmc);
> > +	if (ret)
> > +		dev_err(&pdev->dev, "Failed to create PMC devices\n");
> > +
> > +	return ret;
> > +}
> > +
> > +static struct platform_driver intel_pmc_driver = {
> > +	.probe = intel_pmc_probe,
> > +	.driver = {
> > +		.name = "intel_pmc_bxt",
> > +		.acpi_match_table = intel_pmc_acpi_ids,
> > +		.dev_groups = intel_pmc_groups,
> > +	},
> > +};
> > +
> 
> Remove this line.

Okay.

> > +module_platform_driver(intel_pmc_driver);
> > +
> > +MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
> > +MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
> > +MODULE_DESCRIPTION("Intel Broxton PMC driver");
> > +MODULE_LICENSE("GPL v2");
> 
> [...]
> 
> > diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
> > new file mode 100644
> > index 000000000000..a5fb41910d78
> > --- /dev/null
> > +++ b/include/linux/mfd/intel_pmc_bxt.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef MFD_INTEL_PMC_BXT_H
> > +#define MFD_INTEL_PMC_BXT_H
> > +
> > +#include <linux/types.h>
> > +
> > +/* GCR reg offsets from GCR base */
> > +#define PMC_GCR_PMC_CFG_REG		0x08
> > +#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
> > +#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> > +
> > +/*
> > + * Pointer to the PMC device can be obtained by calling
> > + * dev_get_drvdata() to the parent MFD device.
> > + */
> > +struct intel_pmc_dev;
> 
> Don't you have a shared header file you can put the definition in
> instead?

Unfortunately no. This one is the shared header.

> > +int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
> > +int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
> > +
> > +#endif /* MFD_INTEL_PMC_BXT_H */
> 
> -- 
> Lee Jones [李琼斯]
> Linaro Services Technical Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD
  2020-02-26 10:33     ` Mika Westerberg
@ 2020-02-26 11:23       ` Lee Jones
  2020-02-26 12:22         ` Mika Westerberg
  0 siblings, 1 reply; 30+ messages in thread
From: Lee Jones @ 2020-02-26 11:23 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Wed, 26 Feb 2020, Mika Westerberg wrote:

> On Wed, Feb 26, 2020 at 08:47:49AM +0000, Lee Jones wrote:
> > On Mon, 17 Feb 2020, Mika Westerberg wrote:
> > 
> > > This driver only creates a bunch of platform devices sharing resources
> > > belonging to the PMC device. This is pretty much what MFD subsystem is
> > > for so move the driver there, renaming it to intel_pmc_bxt.c which
> > > should be more clear what it is.
> > > 
> > > MFD subsystem provides nice helper APIs for subdevice creation so
> > > convert the driver to use those. Unfortunately the ACPI device includes
> > > separate resources for most of the subdevices so we cannot simply call
> > > mfd_add_devices() to create all of them but instead we need to call it
> > > separately for each device.
> > > 
> > > The new MFD driver continues to expose two sysfs attributes that allow
> > > userspace to send IPC commands to the PMC/SCU to avoid breaking any
> > > existing applications that may use these. Generally this is bad idea so
> > > document this in the ABI documentation.
> > > 
> > > Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> > > Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > > ---
> > >  .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
> > >  arch/x86/include/asm/intel_pmc_ipc.h          |  47 --
> > >  arch/x86/include/asm/intel_telemetry.h        |   1 +
> > >  drivers/mfd/Kconfig                           |  16 +-
> > >  drivers/mfd/Makefile                          |   1 +
> > >  drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++++++
> > >  drivers/platform/x86/Kconfig                  |  16 +-
> > >  drivers/platform/x86/Makefile                 |   1 -
> > >  drivers/platform/x86/intel_pmc_ipc.c          | 645 ------------------
> > >  .../platform/x86/intel_telemetry_debugfs.c    |  12 +-
> > >  drivers/platform/x86/intel_telemetry_pltdrv.c |   2 +
> > >  drivers/usb/typec/tcpm/Kconfig                |   2 +-
> > >  include/linux/mfd/intel_pmc_bxt.h             |  21 +
> > >  13 files changed, 565 insertions(+), 710 deletions(-)
> > >  create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
> > >  delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
> > >  create mode 100644 drivers/mfd/intel_pmc_bxt.c
> > >  delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
> > >  create mode 100644 include/linux/mfd/intel_pmc_bxt.h
> > 
> > [...]

[...]

> > > +struct intel_pmc_dev {
> > > +	struct device *dev;
> > > +	struct intel_scu_ipc_dev *scu;
> > > +
> > > +	struct mfd_cell cells[PMC_TELEM + 1];
> > 
> > Nicer to add a "PMC_DEVICE_MAX" enum and use that.
> > 
> > Why do these even need to be in here?
> 
> They need to be here because we need to fill them in dynamically based
> on the resources we get from the ACPI device.

Why can't you do that with statically defined structs?

HINT: You can.

> > I would normally suggest creating a cell per device.
> 
> You mean 
> 
> struct intel_pmc_dev {
> 	...
> 	struct mfd_cell tco_cell;
> 	struct mfd_cell punit_cell;
> 	...
> 
> right? Sure no problem.

No.  We don't usually put them in device data at all.

I mean:

static struct mfd_cell tco_cell[] = {
        {      }
};

static struct mfd_cell tco_cell[] = {
        {      }
};

[...]

> > > +static const struct mfd_cell tco = {
> > > +	.name = TCO_DEVICE_NAME,
> > 
> > Use proper string please.
> > 
> > > +	.ignore_resource_conflicts = true,
> > 
> > Why not add tco_pdata here?
> 
> Because we need to pass it the private PMC pointer that is filled later
> on. It is being used by the iTCO_wdt .update_no_reboot_bit() callback as
> its private data.

Just drop the const.

> > > +};
> > > +
> > > +static const struct resource telem_res[] = {
> > > +	DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > > +	DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > > +};
> > > +
> > > +static const struct mfd_cell telem = {
> > > +	.name = TELEMETRY_DEVICE_NAME,
> > 
> > Use proper string please.
> 
> Okay.
> 
> > > +	.resources = telem_res,
> > > +	.num_resources = ARRAY_SIZE(telem_res),
> > > +};
> > > +
> > > +static int intel_pmc_get_tco_resources(struct platform_device *pdev,
> > > +				       struct intel_pmc_dev *pmc)
> > > +{
> > > +	struct itco_wdt_platform_data *pdata;
> > > +	struct resource *res, *tco_res;
> > > +
> > > +	if (acpi_has_watchdog())
> > > +		return 0;
> > > +
> > > +	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 -EINVAL;
> > > +	}
> > > +
> > > +	tco_res = devm_kcalloc(&pdev->dev, 2, sizeof(*tco_res), GFP_KERNEL);
> > > +	if (!tco_res)
> > > +		return -ENOMEM;
> > > +
> > > +	tco_res[0].flags = IORESOURCE_IO;
> > > +	tco_res[0].start = res->start + TCO_BASE_OFFSET;
> > > +	tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
> > > +	tco_res[1].flags = IORESOURCE_IO;
> > > +	tco_res[1].start = res->start + SMI_EN_OFFSET;
> > > +	tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
> > > +
> > > +	pmc->cells[PMC_TCO].resources = tco_res;
> > > +	pmc->cells[PMC_TCO].num_resources = 2;
> > > +
> > > +	pdata = devm_kmemdup(&pdev->dev, &tco_pdata, sizeof(*pdata), GFP_KERNEL);
> > > +	if (!pdata)
> > > +		return -ENOMEM;
> > > +
> > > +	pdata->no_reboot_priv = pmc;
> > 
> > This looks hacky.  What are you doing here?
> 
> So the pmc instance is created per device as you requested. This one
> passes it to the iTCO_wdt .update_no_reboot_bit() callback which we
> implemented in this driver (sane name update_no_reboot_bit()).
> 
> The iTCO_wdt platform data can be found in this header if you want to
> take a look: include/linux/platform_data/itco_wdt.h.

We usually pass these kinds of pointers via device data, rather than
platform data.

> > > +	pmc->cells[PMC_TCO].platform_data = pdata;
> > > +	pmc->cells[PMC_TCO].pdata_size = sizeof(*pdata);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int intel_pmc_get_resources(struct platform_device *pdev,
> > > +				   struct intel_pmc_dev *pmc,
> > > +				   struct intel_scu_ipc_pdata *pdata)
> > > +{
> > > +	struct resource *res, *punit_res;
> > > +	struct resource gcr_res;
> > > +	size_t npunit_res = 0;
> > > +	int ret;
> > > +
> > > +	pdata->irq = platform_get_irq_optional(pdev, 0);
> > > +
> > > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > > +				    PLAT_RESOURCE_IPC_INDEX);
> > > +	if (!res) {
> > > +		dev_err(&pdev->dev, "Failed to get IPC resource\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	/* IPC registers */
> > > +	pdata->mem.flags = res->flags;
> > > +	pdata->mem.start = res->start;
> > > +	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
> > 
> > Passing register addresses through pdata also looks like a hack.
> > 
> > Why not pass via resources?
> 
> It is not actual "platform data" but just a structure that we pass for
> the IPC registration function that then creates the underlying device
> for the SCU IPC using these. Since it is plain device (not struct
> platform device) it does not have the concept of "resources" such as
> platform devices have.

Calling something platform data that isn't platform data is confusing.

Why aren't you using the standard device driver model to register this
device?

[...]

> > > diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
> > > new file mode 100644
> > > index 000000000000..a5fb41910d78
> > > --- /dev/null
> > > +++ b/include/linux/mfd/intel_pmc_bxt.h
> > > @@ -0,0 +1,21 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +#ifndef MFD_INTEL_PMC_BXT_H
> > > +#define MFD_INTEL_PMC_BXT_H
> > > +
> > > +#include <linux/types.h>
> > > +
> > > +/* GCR reg offsets from GCR base */
> > > +#define PMC_GCR_PMC_CFG_REG		0x08
> > > +#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
> > > +#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> > > +
> > > +/*
> > > + * Pointer to the PMC device can be obtained by calling
> > > + * dev_get_drvdata() to the parent MFD device.
> > > + */
> > > +struct intel_pmc_dev;
> > 
> > Don't you have a shared header file you can put the definition in
> > instead?
> 
> Unfortunately no. This one is the shared header.

Please consider moving the definition into here then.

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD
  2020-02-26 11:23       ` Lee Jones
@ 2020-02-26 12:22         ` Mika Westerberg
  0 siblings, 0 replies; 30+ messages in thread
From: Mika Westerberg @ 2020-02-26 12:22 UTC (permalink / raw)
  To: Lee Jones
  Cc: Andy Shevchenko, Darren Hart, Greg Kroah-Hartman,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, H . Peter Anvin,
	x86, Zha Qipeng, David E . Box, Guenter Roeck, Heikki Krogerus,
	Wim Van Sebroeck, platform-driver-x86, linux-kernel

On Wed, Feb 26, 2020 at 11:23:24AM +0000, Lee Jones wrote:
> On Wed, 26 Feb 2020, Mika Westerberg wrote:
> 
> > On Wed, Feb 26, 2020 at 08:47:49AM +0000, Lee Jones wrote:
> > > On Mon, 17 Feb 2020, Mika Westerberg wrote:
> > > 
> > > > This driver only creates a bunch of platform devices sharing resources
> > > > belonging to the PMC device. This is pretty much what MFD subsystem is
> > > > for so move the driver there, renaming it to intel_pmc_bxt.c which
> > > > should be more clear what it is.
> > > > 
> > > > MFD subsystem provides nice helper APIs for subdevice creation so
> > > > convert the driver to use those. Unfortunately the ACPI device includes
> > > > separate resources for most of the subdevices so we cannot simply call
> > > > mfd_add_devices() to create all of them but instead we need to call it
> > > > separately for each device.
> > > > 
> > > > The new MFD driver continues to expose two sysfs attributes that allow
> > > > userspace to send IPC commands to the PMC/SCU to avoid breaking any
> > > > existing applications that may use these. Generally this is bad idea so
> > > > document this in the ABI documentation.
> > > > 
> > > > Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> > > > Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > > > ---
> > > >  .../ABI/obsolete/sysfs-driver-intel_pmc_bxt   |  22 +
> > > >  arch/x86/include/asm/intel_pmc_ipc.h          |  47 --
> > > >  arch/x86/include/asm/intel_telemetry.h        |   1 +
> > > >  drivers/mfd/Kconfig                           |  16 +-
> > > >  drivers/mfd/Makefile                          |   1 +
> > > >  drivers/mfd/intel_pmc_bxt.c                   | 489 +++++++++++++
> > > >  drivers/platform/x86/Kconfig                  |  16 +-
> > > >  drivers/platform/x86/Makefile                 |   1 -
> > > >  drivers/platform/x86/intel_pmc_ipc.c          | 645 ------------------
> > > >  .../platform/x86/intel_telemetry_debugfs.c    |  12 +-
> > > >  drivers/platform/x86/intel_telemetry_pltdrv.c |   2 +
> > > >  drivers/usb/typec/tcpm/Kconfig                |   2 +-
> > > >  include/linux/mfd/intel_pmc_bxt.h             |  21 +
> > > >  13 files changed, 565 insertions(+), 710 deletions(-)
> > > >  create mode 100644 Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
> > > >  delete mode 100644 arch/x86/include/asm/intel_pmc_ipc.h
> > > >  create mode 100644 drivers/mfd/intel_pmc_bxt.c
> > > >  delete mode 100644 drivers/platform/x86/intel_pmc_ipc.c
> > > >  create mode 100644 include/linux/mfd/intel_pmc_bxt.h
> > > 
> > > [...]
> 
> [...]
> 
> > > > +struct intel_pmc_dev {
> > > > +	struct device *dev;
> > > > +	struct intel_scu_ipc_dev *scu;
> > > > +
> > > > +	struct mfd_cell cells[PMC_TELEM + 1];
> > > 
> > > Nicer to add a "PMC_DEVICE_MAX" enum and use that.
> > > 
> > > Why do these even need to be in here?
> > 
> > They need to be here because we need to fill them in dynamically based
> > on the resources we get from the ACPI device.
> 
> Why can't you do that with statically defined structs?
> 
> HINT: You can.

Well if I did that then there are two issues I see. First is that if
there will be another PMC device in the future, we could not cope with
that because the first device that is probed will fill those cells with
its information.

Second is that if we ignore the possibility of multiple devices then we
still end up filling the module wide cells with the resources which does
not seem right to me because we have a intel_pmc_dev instance
separately.

So we can't really use the "standard" MFD way here where we have static
const cells (module wide) that we pass to the MFD core with base address
because the resources in the ACPI device (that the driver binds to) are
not organized like that. The resources are pretty much arbitrary.

> > > I would normally suggest creating a cell per device.
> > 
> > You mean 
> > 
> > struct intel_pmc_dev {
> > 	...
> > 	struct mfd_cell tco_cell;
> > 	struct mfd_cell punit_cell;
> > 	...
> > 
> > right? Sure no problem.
> 
> No.  We don't usually put them in device data at all.
> 
> I mean:
> 
> static struct mfd_cell tco_cell[] = {
>         {      }
> };
> 
> static struct mfd_cell tco_cell[] = {
>         {      }
> };

OK, then my above comment hopefully explains why I can't do that.

> [...]
> 
> > > > +static const struct mfd_cell tco = {
> > > > +	.name = TCO_DEVICE_NAME,
> > > 
> > > Use proper string please.
> > > 
> > > > +	.ignore_resource_conflicts = true,
> > > 
> > > Why not add tco_pdata here?
> > 
> > Because we need to pass it the private PMC pointer that is filled later
> > on. It is being used by the iTCO_wdt .update_no_reboot_bit() callback as
> > its private data.
> 
> Just drop the const.

But again we have the same issue here. So what we have is:

  - module wide resources (the cell templates)
  - per PMC instance resources created when the driver is probed against
    the ACPI device.

I don't think it is good idea to put the latter to the former because of
the reasons I exlain above.

If this would have something like single MMIO register that is split for
the child-devices this would work fine but in this case it unfortunately
does not. Well unless I'm missing something ;-)

> > > > +};
> > > > +
> > > > +static const struct resource telem_res[] = {
> > > > +	DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > > > +	DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
> > > > +};
> > > > +
> > > > +static const struct mfd_cell telem = {
> > > > +	.name = TELEMETRY_DEVICE_NAME,
> > > 
> > > Use proper string please.
> > 
> > Okay.
> > 
> > > > +	.resources = telem_res,
> > > > +	.num_resources = ARRAY_SIZE(telem_res),
> > > > +};
> > > > +
> > > > +static int intel_pmc_get_tco_resources(struct platform_device *pdev,
> > > > +				       struct intel_pmc_dev *pmc)
> > > > +{
> > > > +	struct itco_wdt_platform_data *pdata;
> > > > +	struct resource *res, *tco_res;
> > > > +
> > > > +	if (acpi_has_watchdog())
> > > > +		return 0;
> > > > +
> > > > +	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 -EINVAL;
> > > > +	}
> > > > +
> > > > +	tco_res = devm_kcalloc(&pdev->dev, 2, sizeof(*tco_res), GFP_KERNEL);
> > > > +	if (!tco_res)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	tco_res[0].flags = IORESOURCE_IO;
> > > > +	tco_res[0].start = res->start + TCO_BASE_OFFSET;
> > > > +	tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
> > > > +	tco_res[1].flags = IORESOURCE_IO;
> > > > +	tco_res[1].start = res->start + SMI_EN_OFFSET;
> > > > +	tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
> > > > +
> > > > +	pmc->cells[PMC_TCO].resources = tco_res;
> > > > +	pmc->cells[PMC_TCO].num_resources = 2;
> > > > +
> > > > +	pdata = devm_kmemdup(&pdev->dev, &tco_pdata, sizeof(*pdata), GFP_KERNEL);
> > > > +	if (!pdata)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	pdata->no_reboot_priv = pmc;
> > > 
> > > This looks hacky.  What are you doing here?
> > 
> > So the pmc instance is created per device as you requested. This one
> > passes it to the iTCO_wdt .update_no_reboot_bit() callback which we
> > implemented in this driver (sane name update_no_reboot_bit()).
> > 
> > The iTCO_wdt platform data can be found in this header if you want to
> > take a look: include/linux/platform_data/itco_wdt.h.
> 
> We usually pass these kinds of pointers via device data, rather than
> platform data.

I know but this is already existing iTCO_wdt interface that I'm using.
Not created by this patch series.

> > > > +	pmc->cells[PMC_TCO].platform_data = pdata;
> > > > +	pmc->cells[PMC_TCO].pdata_size = sizeof(*pdata);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +static int intel_pmc_get_resources(struct platform_device *pdev,
> > > > +				   struct intel_pmc_dev *pmc,
> > > > +				   struct intel_scu_ipc_pdata *pdata)
> > > > +{
> > > > +	struct resource *res, *punit_res;
> > > > +	struct resource gcr_res;
> > > > +	size_t npunit_res = 0;
> > > > +	int ret;
> > > > +
> > > > +	pdata->irq = platform_get_irq_optional(pdev, 0);
> > > > +
> > > > +	res = platform_get_resource(pdev, IORESOURCE_MEM,
> > > > +				    PLAT_RESOURCE_IPC_INDEX);
> > > > +	if (!res) {
> > > > +		dev_err(&pdev->dev, "Failed to get IPC resource\n");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	/* IPC registers */
> > > > +	pdata->mem.flags = res->flags;
> > > > +	pdata->mem.start = res->start;
> > > > +	pdata->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
> > > 
> > > Passing register addresses through pdata also looks like a hack.
> > > 
> > > Why not pass via resources?
> > 
> > It is not actual "platform data" but just a structure that we pass for
> > the IPC registration function that then creates the underlying device
> > for the SCU IPC using these. Since it is plain device (not struct
> > platform device) it does not have the concept of "resources" such as
> > platform devices have.
> 
> Calling something platform data that isn't platform data is confusing.
> 
> Why aren't you using the standard device driver model to register this
> device?

Well I think I am. It is now registered the same way as you would
register say, an input device (you don't really have drivers binding for
anything you register with input_register_device()). It even registers a
new class of devices as discussed previously.

> [...]
> 
> > > > diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
> > > > new file mode 100644
> > > > index 000000000000..a5fb41910d78
> > > > --- /dev/null
> > > > +++ b/include/linux/mfd/intel_pmc_bxt.h
> > > > @@ -0,0 +1,21 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > > +#ifndef MFD_INTEL_PMC_BXT_H
> > > > +#define MFD_INTEL_PMC_BXT_H
> > > > +
> > > > +#include <linux/types.h>
> > > > +
> > > > +/* GCR reg offsets from GCR base */
> > > > +#define PMC_GCR_PMC_CFG_REG		0x08
> > > > +#define PMC_GCR_TELEM_DEEP_S0IX_REG	0x78
> > > > +#define PMC_GCR_TELEM_SHLW_S0IX_REG	0x80
> > > > +
> > > > +/*
> > > > + * Pointer to the PMC device can be obtained by calling
> > > > + * dev_get_drvdata() to the parent MFD device.
> > > > + */
> > > > +struct intel_pmc_dev;
> > > 
> > > Don't you have a shared header file you can put the definition in
> > > instead?
> > 
> > Unfortunately no. This one is the shared header.
> 
> Please consider moving the definition into here then.

Okay.

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

end of thread, other threads:[~2020-02-26 12:23 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-17 13:14 [PATCH v6 00/19] platform/x86: Rework intel_scu_ipc and intel_pmc_ipc drivers Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 01/19] platform/x86: intel_scu_ipc: Split out SCU IPC functionality from the SCU driver Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 02/19] platform/x86: intel_scu_ipc: Log more information if SCU IPC command fails Mika Westerberg
2020-02-17 14:08   ` Andy Shevchenko
2020-02-17 13:14 ` [PATCH v6 03/19] platform/x86: intel_scu_ipc: Move legacy SCU IPC API to a separate header Mika Westerberg
2020-02-17 14:11   ` Andy Shevchenko
2020-02-17 13:14 ` [PATCH v6 04/19] platform/x86: intel_scu_ipc: Introduce new SCU IPC API Mika Westerberg
2020-02-17 14:11   ` Andy Shevchenko
2020-02-17 13:14 ` [PATCH v6 05/19] platform/x86: intel_mid_powerbtn: Convert to use " Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 06/19] watchdog: intel-mid_wdt: " Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 07/19] platform/x86: intel_scu_ipcutil: " Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 08/19] platform/x86: intel_scu_ipc: Add managed function to register SCU IPC Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 09/19] platform/x86: intel_pmc_ipc: Start using " Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 10/19] mfd: intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic Mika Westerberg
2020-02-24 15:30   ` Lee Jones
2020-02-17 13:14 ` [PATCH v6 11/19] mfd: intel_soc_pmic_bxtwc: Convert to use new SCU IPC API Mika Westerberg
2020-02-25  9:24   ` Lee Jones
2020-02-17 13:14 ` [PATCH v6 12/19] mfd: intel_soc_pmic_mrfld: " Mika Westerberg
2020-02-26  6:45   ` Lee Jones
2020-02-17 13:14 ` [PATCH v6 13/19] platform/x86: intel_telemetry: " Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 14/19] platform/x86: intel_pmc_ipc: Drop intel_pmc_ipc_command() Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 15/19] x86/platform/intel-mid: Add empty stubs for intel_scu_devices_[create|destroy]() Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 16/19] platform/x86: intel_pmc_ipc: Move PCI IDs to intel_scu_pcidrv.c Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 17/19] platform/x86: intel_telemetry: Add telemetry_get_pltdata() Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 18/19] platform/x86: intel_pmc_ipc: Convert to MFD Mika Westerberg
2020-02-26  8:47   ` Lee Jones
2020-02-26 10:33     ` Mika Westerberg
2020-02-26 11:23       ` Lee Jones
2020-02-26 12:22         ` Mika Westerberg
2020-02-17 13:14 ` [PATCH v6 19/19] MAINTAINERS: Update entry for Intel Broxton PMC driver Mika Westerberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).