Skip to contentSkip to conversation

PMC/PUNIT IPC driver cleanup

RFC Superseded [email protected] 4 participants 22 messages Aug 1, 2017 – Aug 22, 2017

Subsystems (2)

v1 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

cover PMC/PUNIT IPC driver cleanup

From: Kuppuswamy Sathyanarayanan <[email protected]>

Hi All,

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

For now, Since we don't have any good test platform for SCU, I am not planning to modify intel_scu_ipc.c.

This patch-set addresses following issues(except #4) in intel_pmc_ipc and intel_punit_ipc drivers.

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

Patch #1, #2 fixes the issue #1.

Patch #3 fixes the issue #2.

Patch #4, #5, #6 fixes the issue #3.

To fix issue #4 we might need to make changes to drivers that use IPC APIs. So I am not sure whether its worth the effort. Maintainers, let me know your inputs.

If we have to address it, then we might need to adapt to model similar to extcon framework.

ipc_dev = intel_ipc_get_dev("intel_pmc_ipc");

PMC IPC call would look like,

intel_pmc_ipc_command(ipc_dev, cmd, sub, in, inlen, out, outlen)

More info on adapted solution (for issue #1):
----------------------------------------------

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

API for creating a new IPC channel device is,

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

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

For example, after using this new model, PMC ipc comand send routine will look like,

int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out, u32 outlen)

{
    // this function is implemented in generic Intel IPC class driver.
    return ipc_dev_raw_cmd(ipcdev.pmc_ipc_dev, f(cmd, sub), in, inlen, out, outlen, 0, 0);
}

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

Kuppuswamy Sathyanarayanan (6):
platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe() platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe platform/x86: intel_pmc_ipc: Use MFD framework to create dependent
devices
platform: x86: Add generic Intel IPC driver platform/x86: intel_punit_ipc: Use generic intel ipc device calls platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls

 arch/x86/include/asm/intel_ipc_dev.h   | 148 +++++++
 drivers/platform/x86/Kconfig           |   8 +
 drivers/platform/x86/Makefile          |   1 +
 drivers/platform/x86/intel_ipc_dev.c   | 433 ++++++++++++++++++++
 drivers/platform/x86/intel_pmc_ipc.c   | 715 ++++++++++++---------------------
 drivers/platform/x86/intel_punit_ipc.c | 234 ++++-------
 6 files changed, 930 insertions(+), 609 deletions(-)
 create mode 100644 arch/x86/include/asm/intel_ipc_dev.h
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
Signature · 1 line
2.7.4
#1 lore raw

RFC 1/6 platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe()

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch adds proper error handling for failure cases in ipc_pci_probe() function.

---
 drivers/platform/x86/intel_pmc_ipc.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +18 -5
index bb792a5..7b65237 100644
@@ -489,33 +489,46 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	ret = pci_enable_device(pdev);
 	if (ret)
-		return ret;
+		goto release_device;
 
 	ret = pci_request_regions(pdev, "intel_pmc_ipc");
 	if (ret)
-		return ret;
+		goto disable_device;
 
 	pci_resource = pci_resource_start(pdev, 0);
 	len = pci_resource_len(pdev, 0);
 	if (!pci_resource || !len) {
 		dev_err(&pdev->dev, "Failed to get resource\n");
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto free_pci_resources;
 	}
 
 	init_completion(&ipcdev.cmd_complete);
 
 	if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
-		return -EBUSY;
+		ret = -EBUSY;
+		goto free_pci_resources;
 	}
 
 	ipcdev.ipc_base = ioremap_nocache(pci_resource, len);
 	if (!ipcdev.ipc_base) {
 		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
-		free_irq(pdev->irq, &ipcdev);
 		ret = -ENOMEM;
+		goto free_irq;
 	}
 
+	return 0;
+
+free_irq:
+	free_irq(pdev->irq, &ipcdev);
+free_pci_resources:
+	pci_release_regions(pdev);
+disable_device:
+	pci_disable_device(pdev);
+release_device:
+	pci_dev_put(pdev);
+
 	return ret;
 }
 
Signature · 1 line
2.7.4
#2 lore raw

Re: [RFC v1 1/6] platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe()

Quoted 15 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch adds proper error handling for failure cases in ipc_pci_probe() function.

---
 drivers/platform/x86/intel_pmc_ipc.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +0 -0
index bb792a5..7b65237 100644

drivers/platform/x86/intel_pmc_ipc.c static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) · line 492

Quoted 5 lines
drivers/platform/x86/intel_pmc_ipc.c +1 -1
@@ -489,33 +489,46 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        ret = pci_enable_device(pdev);
        if (ret)
-               return ret;
+               goto release_device;

Instead of doing this way and ping-ponging code in the same series, better to switch to managed PCI functions in the first place.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#3 lore raw

Re: [RFC v1 1/6] platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe()

Hi Andy,

Quoted 18 lines · Andy Shevchenko
Quoted 16 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch adds proper error handling for failure cases in ipc_pci_probe() function.

---
  drivers/platform/x86/intel_pmc_ipc.c | 23 ++++++++++++++++++-----
  1 file changed, 18 insertions(+), 5 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +0 -0
index bb792a5..7b65237 100644
@@ -489,33 +489,46 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
Quoted 6 lines

drivers/platform/x86/intel_pmc_ipc.c static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) · line 492

Quoted 4 lines
drivers/platform/x86/intel_pmc_ipc.c +1 -1
         ret = pci_enable_device(pdev);
         if (ret)
-               return ret;
+               goto release_device;

Instead of doing this way and ping-ponging code in the same series, better to switch to managed PCI functions in the first place.

ok. I will merge this patch and "Use devm_* calls in driver probe" patch into a single patch.

Quoted 1 line

-
Sathya

#4 lore raw

RFC 2/6 platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe

In reply to [email protected]

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch cleans up unnecessary free/alloc calls in this driver by using devm_* calls.

---
 drivers/platform/x86/intel_pmc_ipc.c | 68 +++++++++++-------------------------
 1 file changed, 21 insertions(+), 47 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +21 -47
index 7b65237..ad0416e 100644
@@ -505,23 +505,22 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	init_completion(&ipcdev.cmd_complete);
 
-	if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+	if (devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
+				&ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
 		ret = -EBUSY;
 		goto free_pci_resources;
 	}
 
-	ipcdev.ipc_base = ioremap_nocache(pci_resource, len);
+	ipcdev.ipc_base = devm_ioremap_nocache(&pdev->dev, pci_resource, len);
 	if (!ipcdev.ipc_base) {
 		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
 		ret = -ENOMEM;
-		goto free_irq;
+		goto free_pci_resources;
 	}
 
 	return 0;
 
-free_irq:
-	free_irq(pdev->irq, &ipcdev);
 free_pci_resources:
 	pci_release_regions(pdev);
 disable_device:
@@ -534,10 +533,8 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 static void ipc_pci_remove(struct pci_dev *pdev)
 {
-	free_irq(pdev->irq, &ipcdev);
 	pci_release_regions(pdev);
 	pci_dev_put(pdev);
-	iounmap(ipcdev.ipc_base);
 	ipcdev.dev = NULL;
 }
 
@@ -862,18 +859,16 @@ static int ipc_plat_get_res(struct platform_device *pdev)
 		dev_err(&pdev->dev, "Failed to get ipc resource\n");
 		return -ENXIO;
 	}
-	size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
-
-	if (!request_mem_region(res->start, size, pdev->name)) {
-		dev_err(&pdev->dev, "Failed to request ipc resource\n");
-		return -EBUSY;
-	}
-	addr = ioremap_nocache(res->start, size);
-	if (!addr) {
-		dev_err(&pdev->dev, "I/O memory remapping failed\n");
-		release_mem_region(res->start, size);
-		return -ENOMEM;
+	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+		    PLAT_RESOURCE_GCR_SIZE - 1);
+
+	addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(addr)) {
+		dev_err(&pdev->dev,
+			"PMC I/O memory remapping failed\n");
+		return PTR_ERR(addr);
 	}
+
 	ipcdev.ipc_base = addr;
 
 	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
@@ -930,7 +925,6 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
 
 static int ipc_plat_probe(struct platform_device *pdev)
 {
-	struct resource *res;
 	int ret;
 
 	ipcdev.dev = &pdev->dev;
@@ -952,61 +946,41 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	ret = ipc_create_pmc_devices();
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
-		goto err_device;
+		return ret;
 	}
 
-	if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
-			"intel_pmc_ipc", &ipcdev)) {
+	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;
+		goto unregister_devices;
 	}
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		goto err_sys;
+		goto unregister_devices;
 	}
 
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-err_sys:
-	free_irq(ipcdev.irq, &ipcdev);
-err_irq:
+
+unregister_devices:
 	platform_device_unregister(ipcdev.tco_dev);
 	platform_device_unregister(ipcdev.punit_dev);
 	platform_device_unregister(ipcdev.telemetry_dev);
-err_device:
-	iounmap(ipcdev.ipc_base);
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_IPC_INDEX);
-	if (res) {
-		release_mem_region(res->start,
-				   PLAT_RESOURCE_IPC_SIZE +
-				   PLAT_RESOURCE_GCR_SIZE);
-	}
+
 	return ret;
 }
 
 static int ipc_plat_remove(struct platform_device *pdev)
 {
-	struct resource *res;
-
 	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	free_irq(ipcdev.irq, &ipcdev);
 	platform_device_unregister(ipcdev.tco_dev);
 	platform_device_unregister(ipcdev.punit_dev);
 	platform_device_unregister(ipcdev.telemetry_dev);
-	iounmap(ipcdev.ipc_base);
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_IPC_INDEX);
-	if (res) {
-		release_mem_region(res->start,
-				   PLAT_RESOURCE_IPC_SIZE +
-				   PLAT_RESOURCE_GCR_SIZE);
-	}
 	ipcdev.dev = NULL;
 	return 0;
 }
Signature · 1 line
2.7.4
#5 lore raw

Re: [RFC v1 2/6] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe

drivers/platform/x86/intel_pmc_ipc.c static int ipc_plat_probe(struct platform_device *pdev) · line 998

Quoted 12 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch cleans up unnecessary free/alloc calls in this driver by using devm_* calls.

static int ipc_plat_remove(struct platform_device *pdev) {

drivers/platform/x86/intel_pmc_ipc.c +0 -3
-       struct resource *res;
-
        sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
 
-       free_irq(ipcdev.irq, &ipcdev);

Can we get an IRQ at any time here?

Quoted 7 lines
        platform_device_unregister(ipcdev.tco_dev);
        platform_device_unregister(ipcdev.punit_dev);
        platform_device_unregister(ipcdev.telemetry_dev);

        ipcdev.dev = NULL;
        return 0;

}

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#6 lore raw

Re: [RFC v1 2/6] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe

Hi,

Quoted 12 lines · Andy Shevchenko

drivers/platform/x86/intel_pmc_ipc.c static int ipc_plat_probe(struct platform_device *pdev) · line 998

Quoted 10 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

This patch cleans up unnecessary free/alloc calls in this driver by using devm_* calls.
static int ipc_plat_remove(struct platform_device *pdev) {

drivers/platform/x86/intel_pmc_ipc.c +0 -3
-       struct resource *res;
-
         sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-       free_irq(ipcdev.irq, &ipcdev);
Quoted 1 line

Can we get an IRQ at any time here?

Yes, if the ipc_plat_probe() is successful then ipcdev.irq will have valid IRQ in all cases.

Quoted 8 lines
Quoted 7 lines
         platform_device_unregister(ipcdev.tco_dev);
         platform_device_unregister(ipcdev.punit_dev);
         platform_device_unregister(ipcdev.telemetry_dev);
         ipcdev.dev = NULL;
         return 0;

}

-
Sathya

#7 lore raw

Re: [RFC v1 2/6] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe

Quoted 14 lines · sathya
Quoted 13 lines · Andy Shevchenko
Quoted 9 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

This patch cleans up unnecessary free/alloc calls in this driver by using devm_* calls.

  static int ipc_plat_remove(struct platform_device *pdev)
  {
drivers/platform/x86/intel_pmc_ipc.c +0 -3
-       struct resource *res;
-
         sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-       free_irq(ipcdev.irq, &ipcdev);
Quoted 4 lines
Quoted 1 line

Can we get an IRQ at any time here?

Yes, if the ipc_plat_probe() is successful then ipcdev.irq will have valid IRQ in all cases.

...which effectively means you can't remove free_irq() here. OTOH you may replace it by explicit devm_free_irq() call.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
         platform_device_unregister(ipcdev.tco_dev);
         platform_device_unregister(ipcdev.punit_dev);
         platform_device_unregister(ipcdev.telemetry_dev);
         ipcdev.dev = NULL;
         return 0;

}

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#8 lore raw

RFC 3/6 platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

In reply to [email protected]

From: Kuppuswamy Sathyanarayanan <[email protected]>

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

---
 drivers/platform/x86/intel_pmc_ipc.c | 376 +++++++++++++----------------------
 1 file changed, 141 insertions(+), 235 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +141 -235
index ad0416e..43533ec 100644
@@ -33,6 +33,7 @@
 #include <linux/suspend.h>
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -105,8 +106,6 @@
 #define TELEM_SSRAM_SIZE		240
 #define TELEM_PMC_SSRAM_OFFSET		0x1B00
 #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
 /* PMC register bit definitions */
 
@@ -115,6 +114,13 @@
 #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
 #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+enum {
+	PMC_IPC_PUNIT_MFD_BLOCK,
+	PMC_IPC_WATCHDOG_MFD_BLOCK,
+	PMC_IPC_TELEMETRY_MFD_BLOCK,
+	PMC_IPC_MAX_MFD_BLOCK
+};
+
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
 	void __iomem *ipc_base;
@@ -123,25 +129,12 @@ static struct intel_pmc_ipc_dev {
 	int cmd;
 	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
 
-	/* punit */
-	struct platform_device *punit_dev;
-
 	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
 	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
 } ipcdev;
 
 static char *ipc_err_sources[] = {
@@ -613,7 +606,7 @@ static const struct attribute_group intel_ipc_group = {
 	.attrs = intel_ipc_attrs,
 };
 
-static struct resource punit_res_array[] = {
+static struct resource punit_ipc_resources[] = {
 	/* Punit BIOS */
 	{
 		.flags = IORESOURCE_MEM,
@@ -637,10 +630,7 @@ static struct resource punit_res_array[] = {
 	},
 };
 
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
+static struct resource watchdog_ipc_resources[] = {
 	/* ACPI - TCO */
 	{
 		.flags = IORESOURCE_IO,
@@ -658,9 +648,7 @@ static struct itco_wdt_platform_data tco_info = {
 	.update_no_reboot_bit = update_no_reboot_bit,
 };
 
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM	0
-#define TELEMETRY_RESOURCE_PMC_SSRAM	1
-static struct resource telemetry_res[] = {
+static struct resource telemetry_ipc_resources[] = {
 	/*Telemetry*/
 	{
 		.flags = IORESOURCE_MEM,
@@ -670,224 +658,152 @@ static struct resource telemetry_res[] = {
 	},
 };
 
-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 = ARRAY_SIZE(punit_res_array),
-		};
-
-	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)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
 {
-	int ret;
-
-	/* If we have ACPI based watchdog use that instead */
+	u8 n = 0;
+	struct mfd_cell *pmc_mfd_cells;
+
+	pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+			(sizeof(*pmc_mfd_cells) * PMC_IPC_MAX_MFD_BLOCK),
+			GFP_KERNEL);
+	if (!pmc_mfd_cells)
+		return -ENOMEM;
+
+	/* Create PUNIT IPC MFD cell */
+	pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+	pmc_mfd_cells[n].id = -1;
+	pmc_mfd_cells[n].num_resources = ARRAY_SIZE(punit_ipc_resources);
+	pmc_mfd_cells[n].resources = punit_ipc_resources;
+	pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+	n++;
+
+	/* If we have ACPI based watchdog use that instead, othewise create
+	 * a MFD cell for iTCO watchdog*/
 	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);
+		pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].platform_data = &tco_info;
+		pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(watchdog_ipc_resources);
+		pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;
 	}
 
 	if (!ipcdev.telem_res_inval) {
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform device\n");
+		pmc_mfd_cells[n].name = TELEMETRY_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(telemetry_ipc_resources);
+		pmc_mfd_cells[n].resources = telemetry_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;
 	}
 
-	return ret;
+	return devm_mfd_add_devices(&pdev->dev, -1, pmc_mfd_cells, n, NULL,
+				    0, NULL);
 }
 
 static int ipc_plat_get_res(struct platform_device *pdev)
 {
-	struct resource *res, *punit_res;
+	struct resource *res;
 	void __iomem *addr;
-	int size;
+	int mindex, pindex = 0;
 
+	/* Get iTCO watchdog resources */
 	res = platform_get_resource(pdev, IORESOURCE_IO,
 				    PLAT_RESOURCE_ACPI_IO_INDEX);
 	if (!res) {
 		dev_err(&pdev->dev, "Failed to get io resource\n");
 		return -ENXIO;
 	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-	/* This is index 1 to cover BIOS interface register */
-	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 = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
-	}
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_IPC_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get ipc resource\n");
-		return -ENXIO;
-	}
-	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
-		    PLAT_RESOURCE_GCR_SIZE - 1);
-
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(addr)) {
-		dev_err(&pdev->dev,
-			"PMC I/O memory remapping failed\n");
-		return PTR_ERR(addr);
-	}
-
-	ipcdev.ipc_base = addr;
-
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-						TELEM_PUNIT_SSRAM_OFFSET;
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-						TELEM_PMC_SSRAM_OFFSET;
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
+	watchdog_ipc_resources[0].start = res->start + TCO_BASE_OFFSET;
+	watchdog_ipc_resources[0].end = res->start +
+		TCO_BASE_OFFSET + TCO_REGS_SIZE - 1;
+	watchdog_ipc_resources[1].start = res->start + SMI_EN_OFFSET;
+	watchdog_ipc_resources[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;
+
+	dev_info(&pdev->dev, "watchdog res 0: %pR\n",
+			&watchdog_ipc_resources[0]);
+	dev_info(&pdev->dev, "watchdog res 1: %pR\n",
+			&watchdog_ipc_resources[1]);
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_GTD_IFACE_INDEX; mindex++) {
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, mindex);
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource %d\n",
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if not valid resource, skip the rest of steps */
+			if (!res) {
+				pindex++;
+				continue;
+			}
+			memcpy(&punit_ipc_resources[pindex], res,
+					sizeof(*res));
+			dev_info(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_ipc_resources[pindex]);
+			pindex++;
+			break;
+		/* Get Telemetry resources */
+		case PLAT_RESOURCE_TELEM_SSRAM_INDEX:
+			if (!res) {
+				dev_warn(&pdev->dev,
+					"Failed to get telemtry sram res\n");
+				ipcdev.telem_res_inval = 1;
+				continue;
+			}
+			telemetry_ipc_resources[0].start = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET;
+			telemetry_ipc_resources[0].end = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+			telemetry_ipc_resources[1].start = res->start +
+				TELEM_PMC_SSRAM_OFFSET;
+			telemetry_ipc_resources[1].end = res->start +
+				TELEM_PMC_SSRAM_OFFSET + TELEM_SSRAM_SIZE - 1;
+
+			dev_info(&pdev->dev, "telemetry punit ssram res: %pR\n",
+					&telemetry_ipc_resources[0]);
+			dev_info(&pdev->dev, "telemetry pmc ssram res: %pR\n",
+					&telemetry_ipc_resources[1]);
+			break;
+		/* Get IPC resources */
+		case PLAT_RESOURCE_IPC_INDEX:
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get IPC resources\n");
+				return -ENXIO;
+			}
+			res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+				    PLAT_RESOURCE_GCR_SIZE - 1);
+			addr = devm_ioremap_resource(&pdev->dev, res);
+			if (IS_ERR(addr)) {
+				dev_err(&pdev->dev,
+					"PMC I/O memory remapping failed\n");
+				return PTR_ERR(addr);
+			}
+			ipcdev.ipc_base = addr;
+			ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
+			dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
+			break;
+		};
 	}
 
 	return 0;
@@ -943,7 +859,7 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
 		return ret;
@@ -952,36 +868,26 @@ static int ipc_plat_probe(struct platform_device *pdev)
 	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
 			     "intel_pmc_ipc", &ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto unregister_devices;
+		return -EBUSY;
 	}
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		goto unregister_devices;
+		return ret;
 	}
 
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-
-unregister_devices:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
 }
 
 static int ipc_plat_remove(struct platform_device *pdev)
 {
 	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
 	ipcdev.dev = NULL;
+
 	return 0;
 }
 
Signature · 1 line
2.7.4
#9 lore raw

RE: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

Quoted 222 lines

-----Original Message-----
From: [email protected] [mailto:platform-driver- [email protected]] On Behalf Of [email protected]
Sent: Tuesday, August 1, 2017 11:44 PM
To: [email protected]; [email protected]; Zha, Qipeng <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; [email protected]; Kuppuswamy Sathyanarayanan <[email protected]>
Subject: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

From: Kuppuswamy Sathyanarayanan
<[email protected]>

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

Signed-off-by: Kuppuswamy Sathyanarayanan
<[email protected]>

---
 drivers/platform/x86/intel_pmc_ipc.c | 376 +++++++++++++-------------------
---
 1 file changed, 141 insertions(+), 235 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +8 -15
index ad0416e..43533ec 100644
@@ -33,6 +33,7 @@
 #include <linux/suspend.h>
 #include <linux/acpi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
 
 #include <asm/intel_pmc_ipc.h>
 
@@ -105,8 +106,6 @@
 #define TELEM_SSRAM_SIZE		240
 #define TELEM_PMC_SSRAM_OFFSET		0x1B00
 #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
 /* PMC register bit definitions */
 
@@ -115,6 +114,13 @@
 #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
 #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+enum {
+	PMC_IPC_PUNIT_MFD_BLOCK,
+	PMC_IPC_WATCHDOG_MFD_BLOCK,
+	PMC_IPC_TELEMETRY_MFD_BLOCK,
+	PMC_IPC_MAX_MFD_BLOCK
+};
+
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
 	void __iomem *ipc_base;
@@ -123,25 +129,12 @@ static struct intel_pmc_ipc_dev {
 	int cmd;
 	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC
 */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
 	/* gcr */
 	void __iomem *gcr_mem_base;
 	bool has_gcr_regs;
 
-	/* punit */
-	struct platform_device *punit_dev;
-
 	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
 	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
 } ipcdev;
 

static char *ipc_err_sources[] = {

drivers/platform/x86/intel_pmc_ipc.c +1 -1
@@ -613,7 +606,7 @@ static const struct attribute_group intel_ipc_group
 = {
 	.attrs = intel_ipc_attrs,
 };
 
-static struct resource punit_res_array[] = {
+static struct resource punit_ipc_resources[] = {
 	/* Punit BIOS */
 	{
drivers/platform/x86/intel_pmc_ipc.c +10 -89
 		.flags = IORESOURCE_MEM,
@@ -637,10 +630,7 @@ static struct resource punit_res_array[] = {
 	},
 };
 
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
+static struct resource watchdog_ipc_resources[] = {
 	/* ACPI - TCO */
 	{
 		.flags = IORESOURCE_IO,
@@ -658,9 +648,7 @@ static struct itco_wdt_platform_data tco_info = {
 	.update_no_reboot_bit = update_no_reboot_bit,  };
 
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM	0
-#define TELEMETRY_RESOURCE_PMC_SSRAM	1
-static struct resource telemetry_res[] = {
+static struct resource telemetry_ipc_resources[] = {
 	/*Telemetry*/
 	{
 		.flags = IORESOURCE_MEM,
@@ -670,224 +658,152 @@ static struct resource telemetry_res[] = {
 	},
 };
 
-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 = ARRAY_SIZE(punit_res_array),
-		};
-
-	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)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
 {
-	int ret;
-
-	/* If we have ACPI based watchdog use that instead */
+	u8 n = 0;
+	struct mfd_cell *pmc_mfd_cells;
+
+	pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+			(sizeof(*pmc_mfd_cells) *
 PMC_IPC_MAX_MFD_BLOCK),
+			GFP_KERNEL);
+	if (!pmc_mfd_cells)

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 680

Quoted 10 lines
drivers/platform/x86/intel_pmc_ipc.c +9 -0
+		return -ENOMEM;
+
+	/* Create PUNIT IPC MFD cell */
+	pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+	pmc_mfd_cells[n].id = -1;
+	pmc_mfd_cells[n].num_resources =
 ARRAY_SIZE(punit_ipc_resources);
+	pmc_mfd_cells[n].resources = punit_ipc_resources;
+	pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+	n++;

Since it's not really a loop, cant the enum values of MFD block be used instead of 'n'? Will be more readable/intuitive.

Quoted 17 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+	/* If we have ACPI based watchdog use that instead, othewise

create

drivers/platform/x86/intel_pmc_ipc.c +1 -3
+	 * a MFD cell for iTCO watchdog*/
 	if (!acpi_has_watchdog()) {
-		ret = ipc_create_tco_device();
-		if (ret) {
-			dev_err(ipcdev.dev, "Failed to add tco platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -7
-			return ret;
-		}
-	}
-
-	ret = ipc_create_punit_device();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 691

Quoted 10 lines
drivers/platform/x86/intel_pmc_ipc.c +9 -1
-		platform_device_unregister(ipcdev.tco_dev);
+		pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].platform_data = &tco_info;
+		pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(watchdog_ipc_resources);
+		pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Quoted 8 lines
drivers/platform/x86/intel_pmc_ipc.c +0 -4
 	}
 
 	if (!ipcdev.telem_res_inval) {
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 701

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+		pmc_mfd_cells[n].name = TELEMETRY_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(telemetry_ipc_resources);
+		pmc_mfd_cells[n].resources = telemetry_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Quoted 123 lines
drivers/platform/x86/intel_pmc_ipc.c +1 -1
 	}
 
-	return ret;
+	return devm_mfd_add_devices(&pdev->dev, -1, pmc_mfd_cells, n,

NULL,

drivers/platform/x86/intel_pmc_ipc.c +4 -13
+				    0, NULL);
 }
 
 static int ipc_plat_get_res(struct platform_device *pdev)  {
-	struct resource *res, *punit_res;
+	struct resource *res;
 	void __iomem *addr;
-	int size;
+	int mindex, pindex = 0;
 
+	/* Get iTCO watchdog resources */
 	res = platform_get_resource(pdev, IORESOURCE_IO,
 				    PLAT_RESOURCE_ACPI_IO_INDEX);
 	if (!res) {
 		dev_err(&pdev->dev, "Failed to get io resource\n");
 		return -ENXIO;
 	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS

data\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -10
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-	/* This is index 1 to cover BIOS interface register */
-	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");

drivers/platform/x86/intel_pmc_ipc.c +0 -38
-		return -ENXIO;
-	}
-	*++punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +0 -28
-	}
-
-	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;
-	}
-	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
-		    PLAT_RESOURCE_GCR_SIZE - 1);
-
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(addr)) {
-		dev_err(&pdev->dev,
-			"PMC I/O memory remapping failed\n");
-		return PTR_ERR(addr);
-	}
-
-	ipcdev.ipc_base = addr;
-
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram

resource\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -4
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-
	TELEM_PUNIT_SSRAM_OFFSET;
drivers/platform/x86/intel_pmc_ipc.c +0 -3
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-
	TELEM_PMC_SSRAM_OFFSET;

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 727

Quoted 8 lines
drivers/platform/x86/intel_pmc_ipc.c +6 -2
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
+	watchdog_ipc_resources[0].start = res->start + TCO_BASE_OFFSET;
+	watchdog_ipc_resources[0].end = res->start +
+		TCO_BASE_OFFSET + TCO_REGS_SIZE - 1;
+	watchdog_ipc_resources[1].start = res->start + SMI_EN_OFFSET;
+	watchdog_ipc_resources[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;

The 0/1 magic may be replaced with MACROS here and elsewhere.

Quoted 8 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+
+	dev_info(&pdev->dev, "watchdog res 0: %pR\n",
+			&watchdog_ipc_resources[0]);
+	dev_info(&pdev->dev, "watchdog res 1: %pR\n",
+			&watchdog_ipc_resources[1]);
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_GTD_IFACE_INDEX;

mindex++) {

Here we are relying on the fact that the GTD_IFACE_INDEX is at offset 7. It's however not by design but a side-effect of how the BIOS places the resources. So perhaps not a good idea to use it, since GTD_IFACE is not meant to be a terminator/max delimiter.

Quoted 137 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,

mindex);

drivers/platform/x86/intel_pmc_ipc.c +10 -0
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource

%d\n",

drivers/platform/x86/intel_pmc_ipc.c +30 -0
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if not valid resource, skip the rest of steps */
+			if (!res) {
+				pindex++;
+				continue;
+			}
+			memcpy(&punit_ipc_resources[pindex], res,
+					sizeof(*res));
+			dev_info(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_ipc_resources[pindex]);
+			pindex++;
+			break;
+		/* Get Telemetry resources */
+		case PLAT_RESOURCE_TELEM_SSRAM_INDEX:
+			if (!res) {
+				dev_warn(&pdev->dev,
+					"Failed to get telemtry sram res\n");
+				ipcdev.telem_res_inval = 1;
+				continue;
+			}
+			telemetry_ipc_resources[0].start = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET;
+			telemetry_ipc_resources[0].end = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+			telemetry_ipc_resources[1].start = res->start +
+				TELEM_PMC_SSRAM_OFFSET;
+			telemetry_ipc_resources[1].end = res->start +
+				TELEM_PMC_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+			dev_info(&pdev->dev, "telemetry punit ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+					&telemetry_ipc_resources[0]);
+			dev_info(&pdev->dev, "telemetry pmc ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +15 -0
+					&telemetry_ipc_resources[1]);
+			break;
+		/* Get IPC resources */
+		case PLAT_RESOURCE_IPC_INDEX:
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get IPC resources\n");
+				return -ENXIO;
+			}
+			res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+				    PLAT_RESOURCE_GCR_SIZE - 1);
+			addr = devm_ioremap_resource(&pdev->dev, res);
+			if (IS_ERR(addr)) {
+				dev_err(&pdev->dev,
+					"PMC I/O memory remapping

failed\n");

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+				return PTR_ERR(addr);
+			}
+			ipcdev.ipc_base = addr;
+			ipcdev.gcr_mem_base = addr +

PLAT_RESOURCE_GCR_OFFSET;

drivers/platform/x86/intel_pmc_ipc.c +1 -0
+			dev_info(&pdev->dev, "PMC IPC resource %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +3 -1
+			break;
+		};
 	}
 
 	return 0;
@@ -943,7 +859,7 @@ static int ipc_plat_probe(struct platform_device
 *pdev)
 		return ret;
 	}
 
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create pmc devices\n");
drivers/platform/x86/intel_pmc_ipc.c +3 -14
 		return ret;
@@ -952,36 +868,26 @@ static int ipc_plat_probe(struct platform_device
 *pdev)
 	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,
 IRQF_NO_SUSPEND,
 			     "intel_pmc_ipc", &ipcdev)) {
 		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto unregister_devices;
+		return -EBUSY;
 	}
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
 			ret);
-		goto unregister_devices;
+		return ret;
 	}
 
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
-
-unregister_devices:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
 }
 
 static int ipc_plat_remove(struct platform_device *pdev)  {
 	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
 	ipcdev.dev = NULL;
+
 	return 0;
 }
 
--

2.7.4

#10 lore raw

Re: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

Quoted 224 lines · Souvik K
Quoted 224 lines

-----Original Message-----
From: [email protected] [mailto:platform-driver- [email protected]] On Behalf Of [email protected]
Sent: Tuesday, August 1, 2017 11:44 PM
To: [email protected]; [email protected]; Zha, Qipeng <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; [email protected]; Kuppuswamy Sathyanarayanan <[email protected]>
Subject: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

From: Kuppuswamy Sathyanarayanan
<[email protected]>

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

Signed-off-by: Kuppuswamy Sathyanarayanan
<[email protected]>

---
  drivers/platform/x86/intel_pmc_ipc.c | 376 +++++++++++++-------------------
---
  1 file changed, 141 insertions(+), 235 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +8 -15
index ad0416e..43533ec 100644
@@ -33,6 +33,7 @@
  #include <linux/suspend.h>
  #include <linux/acpi.h>
  #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
 
  #include <asm/intel_pmc_ipc.h>
 
@@ -105,8 +106,6 @@
  #define TELEM_SSRAM_SIZE		240
  #define TELEM_PMC_SSRAM_OFFSET		0x1B00
  #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
  /* PMC register bit definitions */
 
@@ -115,6 +114,13 @@
  #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
  #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+enum {
+	PMC_IPC_PUNIT_MFD_BLOCK,
+	PMC_IPC_WATCHDOG_MFD_BLOCK,
+	PMC_IPC_TELEMETRY_MFD_BLOCK,
+	PMC_IPC_MAX_MFD_BLOCK
+};
+
  static struct intel_pmc_ipc_dev {
  	struct device *dev;
  	void __iomem *ipc_base;
@@ -123,25 +129,12 @@ static struct intel_pmc_ipc_dev {
  	int cmd;
  	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC
 */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
  	/* gcr */
  	void __iomem *gcr_mem_base;
  	bool has_gcr_regs;
 
-	/* punit */
-	struct platform_device *punit_dev;
-
  	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
  	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
  } ipcdev;
 
  static char *ipc_err_sources[] = {
drivers/platform/x86/intel_pmc_ipc.c +1 -1
@@ -613,7 +606,7 @@ static const struct attribute_group intel_ipc_group
 = {
  	.attrs = intel_ipc_attrs,
  };
 
-static struct resource punit_res_array[] = {
+static struct resource punit_ipc_resources[] = {
  	/* Punit BIOS */
  	{

.flags = IORESOURCE_MEM,

drivers/platform/x86/intel_pmc_ipc.c +13 -89
@@ -637,10 +630,7 @@ static struct resource punit_res_array[] = {
  	},
  };
 
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
+static struct resource watchdog_ipc_resources[] = {
  	/* ACPI - TCO */
  	{
  		.flags = IORESOURCE_IO,
@@ -658,9 +648,7 @@ static struct itco_wdt_platform_data tco_info = {
  	.update_no_reboot_bit = update_no_reboot_bit,  };
 
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM	0
-#define TELEMETRY_RESOURCE_PMC_SSRAM	1
-static struct resource telemetry_res[] = {
+static struct resource telemetry_ipc_resources[] = {
  	/*Telemetry*/
  	{
  		.flags = IORESOURCE_MEM,
@@ -670,224 +658,152 @@ static struct resource telemetry_res[] = {
  	},
  };
 
-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 = ARRAY_SIZE(punit_res_array),
-		};
-
-	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)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
  {
-	int ret;
-
-	/* If we have ACPI based watchdog use that instead */
+	u8 n = 0;
+	struct mfd_cell *pmc_mfd_cells;
+
+	pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+			(sizeof(*pmc_mfd_cells) *
 PMC_IPC_MAX_MFD_BLOCK),
+			GFP_KERNEL);
+	if (!pmc_mfd_cells)
+		return -ENOMEM;
+
+	/* Create PUNIT IPC MFD cell */
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 691

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +6 -0
+	pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+	pmc_mfd_cells[n].id = -1;
+	pmc_mfd_cells[n].num_resources =
 ARRAY_SIZE(punit_ipc_resources);
+	pmc_mfd_cells[n].resources = punit_ipc_resources;
+	pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+	n++;

Since it's not really a loop, cant the enum values of MFD block be used instead of 'n'? Will be more readable/intuitive.

In this case, watchdog and telemetry device creation is optional and hence we can't use fixed index when creating mfd-cells. If one of these devices is not available then the number of valid-mfd-cells and the index number of individual device cell in pmc_mfd_cells array will change.

Quoted 21 lines
Quoted 21 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+	/* If we have ACPI based watchdog use that instead, othewise

create

drivers/platform/x86/intel_pmc_ipc.c +1 -3
+	 * a MFD cell for iTCO watchdog*/
  	if (!acpi_has_watchdog()) {
-		ret = ipc_create_tco_device();
-		if (ret) {
-			dev_err(ipcdev.dev, "Failed to add tco platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -7
-			return ret;
-		}
-	}
-
-	ret = ipc_create_punit_device();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c +2 -1
-		platform_device_unregister(ipcdev.tco_dev);
+		pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 691

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+		pmc_mfd_cells[n].platform_data = &tco_info;
+		pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(watchdog_ipc_resources);
+		pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Same as above.

Quoted 17 lines
Quoted 9 lines
  	}

  	if (!ipcdev.telem_res_inval) {
drivers/platform/x86/intel_pmc_ipc.c +0 -4
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 701

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+		pmc_mfd_cells[n].name = TELEMETRY_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(telemetry_ipc_resources);
+		pmc_mfd_cells[n].resources = telemetry_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Same as above.

Quoted 124 lines
Quoted 124 lines
  	}
drivers/platform/x86/intel_pmc_ipc.c +1 -1
-	return ret;
+	return devm_mfd_add_devices(&pdev->dev, -1, pmc_mfd_cells, n,

NULL,

drivers/platform/x86/intel_pmc_ipc.c +4 -13
+				    0, NULL);
  }
 
  static int ipc_plat_get_res(struct platform_device *pdev)  {
-	struct resource *res, *punit_res;
+	struct resource *res;
  	void __iomem *addr;
-	int size;
+	int mindex, pindex = 0;
 
+	/* Get iTCO watchdog resources */
  	res = platform_get_resource(pdev, IORESOURCE_IO,
  				    PLAT_RESOURCE_ACPI_IO_INDEX);
  	if (!res) {
  		dev_err(&pdev->dev, "Failed to get io resource\n");
  		return -ENXIO;
  	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS

data\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -10
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-	/* This is index 1 to cover BIOS interface register */
-	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");

drivers/platform/x86/intel_pmc_ipc.c +0 -38
-		return -ENXIO;
-	}
-	*++punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +0 -28
-	}
-
-	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;
-	}
-	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
-		    PLAT_RESOURCE_GCR_SIZE - 1);
-
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(addr)) {
-		dev_err(&pdev->dev,
-			"PMC I/O memory remapping failed\n");
-		return PTR_ERR(addr);
-	}
-
-	ipcdev.ipc_base = addr;
-
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram

resource\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -4
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-
	TELEM_PUNIT_SSRAM_OFFSET;
drivers/platform/x86/intel_pmc_ipc.c +0 -3
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-
	TELEM_PMC_SSRAM_OFFSET;
drivers/platform/x86/intel_pmc_ipc.c +0 -1
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 727

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +6 -1
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
+	watchdog_ipc_resources[0].start = res->start + TCO_BASE_OFFSET;
+	watchdog_ipc_resources[0].end = res->start +
+		TCO_BASE_OFFSET + TCO_REGS_SIZE - 1;
+	watchdog_ipc_resources[1].start = res->start + SMI_EN_OFFSET;
+	watchdog_ipc_resources[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;

The 0/1 magic may be replaced with MACROS here and elsewhere.

I think I tried it first and then later removed it because the length of few lines go over and 80.

Quoted 11 lines
Quoted 9 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+
+	dev_info(&pdev->dev, "watchdog res 0: %pR\n",
+			&watchdog_ipc_resources[0]);
+	dev_info(&pdev->dev, "watchdog res 1: %pR\n",
+			&watchdog_ipc_resources[1]);
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_GTD_IFACE_INDEX;

mindex++) {

Here we are relying on the fact that the GTD_IFACE_INDEX is at offset 7. It's however not by design but a side-effect of how the BIOS places the resources. So perhaps not a good idea to use it, since GTD_IFACE is not meant to be a terminator/max delimiter.

We can add a new macro like "#define PLAT_RESOURCE_MAX_INDEX 8" and use it in this loop.

But, In future, If BIOS changes its resource order or removes certain resources then it needs corresponding changes to this driver. And when they fix that issue, they have to make sure the MAX_INDEX value is appropriately modified.

So even using separate macro doesn't completely isolates it from BIOS resource changes.

Quoted 139 lines
Quoted 138 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,

mindex);

drivers/platform/x86/intel_pmc_ipc.c +10 -0
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource

%d\n",

drivers/platform/x86/intel_pmc_ipc.c +30 -0
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if not valid resource, skip the rest of steps */
+			if (!res) {
+				pindex++;
+				continue;
+			}
+			memcpy(&punit_ipc_resources[pindex], res,
+					sizeof(*res));
+			dev_info(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_ipc_resources[pindex]);
+			pindex++;
+			break;
+		/* Get Telemetry resources */
+		case PLAT_RESOURCE_TELEM_SSRAM_INDEX:
+			if (!res) {
+				dev_warn(&pdev->dev,
+					"Failed to get telemtry sram res\n");
+				ipcdev.telem_res_inval = 1;
+				continue;
+			}
+			telemetry_ipc_resources[0].start = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET;
+			telemetry_ipc_resources[0].end = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+			telemetry_ipc_resources[1].start = res->start +
+				TELEM_PMC_SSRAM_OFFSET;
+			telemetry_ipc_resources[1].end = res->start +
+				TELEM_PMC_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+			dev_info(&pdev->dev, "telemetry punit ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+					&telemetry_ipc_resources[0]);
+			dev_info(&pdev->dev, "telemetry pmc ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +15 -0
+					&telemetry_ipc_resources[1]);
+			break;
+		/* Get IPC resources */
+		case PLAT_RESOURCE_IPC_INDEX:
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get IPC resources\n");
+				return -ENXIO;
+			}
+			res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+				    PLAT_RESOURCE_GCR_SIZE - 1);
+			addr = devm_ioremap_resource(&pdev->dev, res);
+			if (IS_ERR(addr)) {
+				dev_err(&pdev->dev,
+					"PMC I/O memory remapping

failed\n");

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+				return PTR_ERR(addr);
+			}
+			ipcdev.ipc_base = addr;
+			ipcdev.gcr_mem_base = addr +

PLAT_RESOURCE_GCR_OFFSET;

drivers/platform/x86/intel_pmc_ipc.c +1 -0
+			dev_info(&pdev->dev, "PMC IPC resource %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +3 -1
+			break;
+		};
  	}
 
  	return 0;
@@ -943,7 +859,7 @@ static int ipc_plat_probe(struct platform_device
 *pdev)
  		return ret;
  	}
 
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to create pmc devices\n");

return ret;

drivers/platform/x86/intel_pmc_ipc.c +3 -14
@@ -952,36 +868,26 @@ static int ipc_plat_probe(struct platform_device
 *pdev)
  	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,
 IRQF_NO_SUSPEND,
  			     "intel_pmc_ipc", &ipcdev)) {
  		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto unregister_devices;
+		return -EBUSY;
  	}
 
  	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
  			ret);
-		goto unregister_devices;
+		return ret;
  	}
 
  	ipcdev.has_gcr_regs = true;
 
  	return 0;
-
-unregister_devices:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
  }
 
  static int ipc_plat_remove(struct platform_device *pdev)  {
  	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
  	ipcdev.dev = NULL;
+
  	return 0;
  }
 
--

2.7.4

Signature · 2 lines
Sathyanarayanan Kuppuswamy
Linux kernel developer
#11 lore raw

RE: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

Quoted 252 lines

-----Original Message-----
From: sathyanarayanan kuppuswamy [mailto:[email protected]]
Sent: Thursday, August 3, 2017 3:29 AM
To: Chakravarty, Souvik K <[email protected]>; [email protected]; [email protected]; Zha, Qipeng <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; [email protected]
Subject: Re: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

Quoted 109 lines · Souvik K
Quoted 109 lines

-----Original Message-----
From: [email protected] [mailto:platform-driver- [email protected]] On Behalf Of [email protected]
Sent: Tuesday, August 1, 2017 11:44 PM
To: [email protected]; [email protected]; Zha, Qipeng <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; [email protected]; Kuppuswamy Sathyanarayanan <[email protected]>
Subject: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

From: Kuppuswamy Sathyanarayanan
<[email protected]>

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

Signed-off-by: Kuppuswamy Sathyanarayanan
<[email protected]>

---
  drivers/platform/x86/intel_pmc_ipc.c | 376
+++++++++++++-------------------
---
  1 file changed, 141 insertions(+), 235 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +8 -15
index ad0416e..43533ec 100644
@@ -33,6 +33,7 @@
  #include <linux/suspend.h>
  #include <linux/acpi.h>
  #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
 
  #include <asm/intel_pmc_ipc.h>
 
@@ -105,8 +106,6 @@
  #define TELEM_SSRAM_SIZE		240
  #define TELEM_PMC_SSRAM_OFFSET		0x1B00
  #define TELEM_PUNIT_SSRAM_OFFSET	0x1A00
-#define TCO_PMC_OFFSET			0x8
-#define TCO_PMC_SIZE			0x4
 
  /* PMC register bit definitions */
 
@@ -115,6 +114,13 @@
  #define PMC_CFG_NO_REBOOT_EN		(1 << 4)
  #define PMC_CFG_NO_REBOOT_DIS		(0 << 4)
 
+enum {
+	PMC_IPC_PUNIT_MFD_BLOCK,
+	PMC_IPC_WATCHDOG_MFD_BLOCK,
+	PMC_IPC_TELEMETRY_MFD_BLOCK,
+	PMC_IPC_MAX_MFD_BLOCK
+};
+
  static struct intel_pmc_ipc_dev {
  	struct device *dev;
  	void __iomem *ipc_base;
@@ -123,25 +129,12 @@ static struct intel_pmc_ipc_dev {
  	int cmd;
  	struct completion cmd_complete;
 
-	/* The following PMC BARs share the same ACPI device with the IPC
 */
-	resource_size_t acpi_io_base;
-	int acpi_io_size;
-	struct platform_device *tco_dev;
-
  	/* gcr */
  	void __iomem *gcr_mem_base;
  	bool has_gcr_regs;
 
-	/* punit */
-	struct platform_device *punit_dev;
-
  	/* Telemetry */
-	resource_size_t telem_pmc_ssram_base;
-	resource_size_t telem_punit_ssram_base;
-	int telem_pmc_ssram_size;
-	int telem_punit_ssram_size;
  	u8 telem_res_inval;
-	struct platform_device *telemetry_dev;
  } ipcdev;
 
  static char *ipc_err_sources[] = {
drivers/platform/x86/intel_pmc_ipc.c +1 -1
@@ -613,7 +606,7 @@ static const struct attribute_group
 intel_ipc_group = {
  	.attrs = intel_ipc_attrs,
  };
 
-static struct resource punit_res_array[] = {
+static struct resource punit_ipc_resources[] = {
  	/* Punit BIOS */
  	{

.flags = IORESOURCE_MEM,

drivers/platform/x86/intel_pmc_ipc.c +0 -0
@@ -637,10 +630,7 @@ static struct resource punit_res_array[] = {
  	},
  };
 
Quoted 9 lines

drivers/platform/x86/intel_pmc_ipc.c static struct itco_wdt_platform_data tco_info =

Quoted 9 lines
drivers/platform/x86/intel_pmc_ipc.c +1 -4
-#define TCO_RESOURCE_ACPI_IO		0
-#define TCO_RESOURCE_SMI_EN_IO		1
-#define TCO_RESOURCE_GCR_MEM		2
-static struct resource tco_res[] = {
+static struct resource watchdog_ipc_resources[] = {
  	/* ACPI - TCO */
  	{
  		.flags = IORESOURCE_IO,
@@ -658,9 +648,7 @@ static struct itco_wdt_platform_data tco_info =

{

Quoted 107 lines
Quoted 107 lines
drivers/platform/x86/intel_pmc_ipc.c +12 -84
  	.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[] = {
+static struct resource telemetry_ipc_resources[] = {
  	/*Telemetry*/
  	{
  		.flags = IORESOURCE_MEM,
@@ -670,224 +658,152 @@ static struct resource telemetry_res[] = {
  	},
  };
 
-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 = ARRAY_SIZE(punit_res_array),
-		};
-
-	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)
+static int ipc_create_pmc_devices(struct platform_device *pdev)
  {
-	int ret;
-
-	/* If we have ACPI based watchdog use that instead */
+	u8 n = 0;
+	struct mfd_cell *pmc_mfd_cells;
+
+	pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+			(sizeof(*pmc_mfd_cells) *
 PMC_IPC_MAX_MFD_BLOCK),
+			GFP_KERNEL);
+	if (!pmc_mfd_cells)
+		return -ENOMEM;
+
+	/* Create PUNIT IPC MFD cell */
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 691

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +6 -0
+	pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+	pmc_mfd_cells[n].id = -1;
+	pmc_mfd_cells[n].num_resources =
 ARRAY_SIZE(punit_ipc_resources);
+	pmc_mfd_cells[n].resources = punit_ipc_resources;
+	pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+	n++;

Since it's not really a loop, cant the enum values of MFD block be used

Quoted 5 lines

instead of 'n'? Will be more readable/intuitive.
In this case, watchdog and telemetry device creation is optional and hence we can't use fixed index when creating mfd-cells. If one of these devices is not available then the number of valid-mfd-cells and the index number of individual device cell in pmc_mfd_cells array will change.

Agreed. I read in a hurry.

Quoted 199 lines
Quoted 21 lines
Quoted 21 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+	/* If we have ACPI based watchdog use that instead, othewise

create

drivers/platform/x86/intel_pmc_ipc.c +1 -3
+	 * a MFD cell for iTCO watchdog*/
  	if (!acpi_has_watchdog()) {
-		ret = ipc_create_tco_device();
-		if (ret) {
-			dev_err(ipcdev.dev, "Failed to add tco platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -7
-			return ret;
-		}
-	}
-
-	ret = ipc_create_punit_device();
-	if (ret) {
-		dev_err(ipcdev.dev, "Failed to add punit platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c +2 -1
-		platform_device_unregister(ipcdev.tco_dev);
+		pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 691

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+		pmc_mfd_cells[n].platform_data = &tco_info;
+		pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(watchdog_ipc_resources);
+		pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Same as above.

Quoted 17 lines
Quoted 9 lines
  	}

  	if (!ipcdev.telem_res_inval) {
drivers/platform/x86/intel_pmc_ipc.c +0 -4
-		ret = ipc_create_telemetry_device();
-		if (ret)
-			dev_warn(ipcdev.dev,
-				"Failed to add telemetry platform

device\n");

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 701

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+		pmc_mfd_cells[n].name = TELEMETRY_DEVICE_NAME;
+		pmc_mfd_cells[n].id = -1;
+		pmc_mfd_cells[n].num_resources =
+			ARRAY_SIZE(telemetry_ipc_resources);
+		pmc_mfd_cells[n].resources = telemetry_ipc_resources;
+		pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+		n++;

Same as above.

Same as above.

Quoted 124 lines
Quoted 124 lines
  	}
drivers/platform/x86/intel_pmc_ipc.c +1 -1
-	return ret;
+	return devm_mfd_add_devices(&pdev->dev, -1, pmc_mfd_cells, n,

NULL,

drivers/platform/x86/intel_pmc_ipc.c +4 -13
+				    0, NULL);
  }
 
  static int ipc_plat_get_res(struct platform_device *pdev)  {
-	struct resource *res, *punit_res;
+	struct resource *res;
  	void __iomem *addr;
-	int size;
+	int mindex, pindex = 0;
 
+	/* Get iTCO watchdog resources */
  	res = platform_get_resource(pdev, IORESOURCE_IO,
  				    PLAT_RESOURCE_ACPI_IO_INDEX);
  	if (!res) {
  		dev_err(&pdev->dev, "Failed to get io resource\n");
  		return -ENXIO;
  	}
-	size = resource_size(res);
-	ipcdev.acpi_io_base = res->start;
-	ipcdev.acpi_io_size = size;
-	dev_info(&pdev->dev, "io res: %pR\n", res);
-
-	punit_res = punit_res_array;
-	/* This is index 0 to cover BIOS data register */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_BIOS_DATA_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get res of punit BIOS

data\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -10
-		return -ENXIO;
-	}
-	*punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-	/* This is index 1 to cover BIOS interface register */
-	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");

drivers/platform/x86/intel_pmc_ipc.c +0 -38
-		return -ENXIO;
-	}
-	*++punit_res = *res;
-	dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-	/* This is index 2 to cover ISP data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-	}
 
-	/* This is index 3 to cover ISP interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_ISP_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-	}
-
-	/* This is index 4 to cover GTD data register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_DATA_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-	}
-
-	/* This is index 5 to cover GTD interface register, optional */
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_GTD_IFACE_INDEX);
-	++punit_res;
-	if (res) {
-		*punit_res = *res;
-		dev_info(&pdev->dev, "punit GTD interface res: %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +0 -28
-	}
-
-	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;
-	}
-	res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
-		    PLAT_RESOURCE_GCR_SIZE - 1);
-
-	addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(addr)) {
-		dev_err(&pdev->dev,
-			"PMC I/O memory remapping failed\n");
-		return PTR_ERR(addr);
-	}
-
-	ipcdev.ipc_base = addr;
-
-	ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-	dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-	ipcdev.telem_res_inval = 0;
-	res = platform_get_resource(pdev, IORESOURCE_MEM,
-				    PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-	if (!res) {
-		dev_err(&pdev->dev, "Failed to get telemetry ssram

resource\n");

drivers/platform/x86/intel_pmc_ipc.c +0 -4
-		ipcdev.telem_res_inval = 1;
-	} else {
-		ipcdev.telem_punit_ssram_base = res->start +
-
	TELEM_PUNIT_SSRAM_OFFSET;
drivers/platform/x86/intel_pmc_ipc.c +0 -3
-		ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-		ipcdev.telem_pmc_ssram_base = res->start +
-
	TELEM_PMC_SSRAM_OFFSET;
drivers/platform/x86/intel_pmc_ipc.c +0 -1
-		ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 727

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +6 -1
-		dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
+	watchdog_ipc_resources[0].start = res->start + TCO_BASE_OFFSET;
+	watchdog_ipc_resources[0].end = res->start +
+		TCO_BASE_OFFSET + TCO_REGS_SIZE - 1;
+	watchdog_ipc_resources[1].start = res->start + SMI_EN_OFFSET;
+	watchdog_ipc_resources[1].end = res->start +
+		SMI_EN_OFFSET + SMI_EN_SIZE - 1;

The 0/1 magic may be replaced with MACROS here and elsewhere.

I think I tried it first and then later removed it because the length of few lines go over and 80.

Quoted 10 lines
Quoted 9 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+
+	dev_info(&pdev->dev, "watchdog res 0: %pR\n",
+			&watchdog_ipc_resources[0]);
+	dev_info(&pdev->dev, "watchdog res 1: %pR\n",
+			&watchdog_ipc_resources[1]);
+
+	for (mindex = 0; mindex <= PLAT_RESOURCE_GTD_IFACE_INDEX;

mindex++) {

Here we are relying on the fact that the GTD_IFACE_INDEX is at offset 7.

It's however not by design but a side-effect of how the BIOS places the resources.

Quoted 1 line

So perhaps not a good idea to use it, since GTD_IFACE is not meant to be

a terminator/max delimiter.
We can add a new macro like "#define PLAT_RESOURCE_MAX_INDEX 8" and use it in this loop.

Quoted 7 lines

But, In future, If BIOS changes its resource order or removes certain resources then it needs corresponding changes to this driver. And when they fix that issue, they have to make sure the MAX_INDEX value is appropriately modified.

So even using separate macro doesn't completely isolates it from BIOS resource changes.

Well you are right as far as changing the MACRO value is.
But using a MAX_INDEX is intuitive and changing the MAX_INDEX value is also intuitive.
OTOH if you maintain the GTD_IFACE, you have to change the loop delimiter to a different IFACE's (e.g. ISP_DATA) every time the BIOS changes and this seems to be a bit strange, esp. when only the index max value has changed. Isn't the above case exactly not what a MAX macro is designed to do?

Quoted 144 lines
Quoted 90 lines
Quoted 90 lines
drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,

mindex);

drivers/platform/x86/intel_pmc_ipc.c +10 -0
+
+		switch (mindex) {
+		/* Get PUNIT resources */
+		case PLAT_RESOURCE_BIOS_DATA_INDEX:
+		case PLAT_RESOURCE_BIOS_IFACE_INDEX:
+			/* BIOS resources are required, so return error if not
+			 * available */
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get punit mem resource

%d\n",

drivers/platform/x86/intel_pmc_ipc.c +30 -0
+					pindex);
+				return -ENXIO;
+			}
+		case PLAT_RESOURCE_ISP_DATA_INDEX:
+		case PLAT_RESOURCE_ISP_IFACE_INDEX:
+		case PLAT_RESOURCE_GTD_DATA_INDEX:
+		case PLAT_RESOURCE_GTD_IFACE_INDEX:
+			/* if not valid resource, skip the rest of steps */
+			if (!res) {
+				pindex++;
+				continue;
+			}
+			memcpy(&punit_ipc_resources[pindex], res,
+					sizeof(*res));
+			dev_info(&pdev->dev, "PUNIT memory res: %pR\n",
+					&punit_ipc_resources[pindex]);
+			pindex++;
+			break;
+		/* Get Telemetry resources */
+		case PLAT_RESOURCE_TELEM_SSRAM_INDEX:
+			if (!res) {
+				dev_warn(&pdev->dev,
+					"Failed to get telemtry sram res\n");
+				ipcdev.telem_res_inval = 1;
+				continue;
+			}
+			telemetry_ipc_resources[0].start = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET;
+			telemetry_ipc_resources[0].end = res->start +
+				TELEM_PUNIT_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+			telemetry_ipc_resources[1].start = res->start +
+				TELEM_PMC_SSRAM_OFFSET;
+			telemetry_ipc_resources[1].end = res->start +
+				TELEM_PMC_SSRAM_OFFSET +

TELEM_SSRAM_SIZE - 1;

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+
+			dev_info(&pdev->dev, "telemetry punit ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+					&telemetry_ipc_resources[0]);
+			dev_info(&pdev->dev, "telemetry pmc ssram res:

%pR\n",

drivers/platform/x86/intel_pmc_ipc.c +15 -0
+					&telemetry_ipc_resources[1]);
+			break;
+		/* Get IPC resources */
+		case PLAT_RESOURCE_IPC_INDEX:
+			if (!res) {
+				dev_err(&pdev->dev,
+					"Failed to get IPC resources\n");
+				return -ENXIO;
+			}
+			res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
+				    PLAT_RESOURCE_GCR_SIZE - 1);
+			addr = devm_ioremap_resource(&pdev->dev, res);
+			if (IS_ERR(addr)) {
+				dev_err(&pdev->dev,
+					"PMC I/O memory remapping

failed\n");

drivers/platform/x86/intel_pmc_ipc.c +4 -0
+				return PTR_ERR(addr);
+			}
+			ipcdev.ipc_base = addr;
+			ipcdev.gcr_mem_base = addr +

PLAT_RESOURCE_GCR_OFFSET;

drivers/platform/x86/intel_pmc_ipc.c +1 -0
+			dev_info(&pdev->dev, "PMC IPC resource %pR\n",

res);

drivers/platform/x86/intel_pmc_ipc.c +2 -0
+			break;
+		};
  	}
 
  	return 0;
@@ -943,7 +859,7 @@ static int ipc_plat_probe(struct platform_device
 *pdev)
  		return ret;
  	}
 
Quoted 9 lines
Quoted 9 lines
drivers/platform/x86/intel_pmc_ipc.c +1 -1
-	ret = ipc_create_pmc_devices();
+	ret = ipc_create_pmc_devices(pdev);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to create pmc devices\n");

return ret;

drivers/platform/x86/intel_pmc_ipc.c +0 -0
@@ -952,36 +868,26 @@ static int ipc_plat_probe(struct

platform_device
*pdev)
if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc,

IRQF_NO_SUSPEND,

Quoted 37 lines
Quoted 37 lines
  			     "intel_pmc_ipc", &ipcdev)) {
  		dev_err(&pdev->dev, "Failed to request irq\n");
drivers/platform/x86/intel_pmc_ipc.c +3 -13
-		ret = -EBUSY;
-		goto unregister_devices;
+		return -EBUSY;
  	}
 
  	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
  	if (ret) {
  		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
  			ret);
-		goto unregister_devices;
+		return ret;
  	}
 
  	ipcdev.has_gcr_regs = true;
 
  	return 0;
-
-unregister_devices:
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
-
-	return ret;
  }
 
  static int ipc_plat_remove(struct platform_device *pdev)  {
  	sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
-	platform_device_unregister(ipcdev.tco_dev);
-	platform_device_unregister(ipcdev.punit_dev);
-	platform_device_unregister(ipcdev.telemetry_dev);
  	ipcdev.dev = NULL;
+
  	return 0;
  }
Quoted 3 lines
Quoted 2 lines

--
2.7.4

--
Sathyanarayanan Kuppuswamy
Linux kernel developer

#12 lore raw

Re: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

In reply to [email protected]
Quoted 15 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

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

drivers/platform/x86/intel_pmc_ipc.c +6 -3
+static int ipc_create_pmc_devices(struct platform_device *pdev)
 {
-       int ret;
-
-       /* If we have ACPI based watchdog use that instead */
+       u8 n = 0;
+       struct mfd_cell *pmc_mfd_cells;
+
+       pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+                       (sizeof(*pmc_mfd_cells) * PMC_IPC_MAX_MFD_BLOCK),

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 677

Quoted 10 lines
drivers/platform/x86/intel_pmc_ipc.c +10 -0
+                       GFP_KERNEL);
+       if (!pmc_mfd_cells)
+               return -ENOMEM;
+
+       /* Create PUNIT IPC MFD cell */
+       pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+       pmc_mfd_cells[n].id = -1;
+       pmc_mfd_cells[n].num_resources = ARRAY_SIZE(punit_ipc_resources);
+       pmc_mfd_cells[n].resources = punit_ipc_resources;
+       pmc_mfd_cells[n].ignore_resource_conflicts = 1;

Please, use static variables instead of allocated on a heap.

Quoted 6 lines
drivers/platform/x86/intel_pmc_ipc.c +4 -0
+       n++;
+
+       /* If we have ACPI based watchdog use that instead, othewise create
+        * a MFD cell for iTCO watchdog*/
        if (!acpi_has_watchdog()) {
 

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 692

Quoted 10 lines
drivers/platform/x86/intel_pmc_ipc.c +9 -0
+               pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+               pmc_mfd_cells[n].id = -1;
+               pmc_mfd_cells[n].platform_data = &tco_info;
+               pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+               pmc_mfd_cells[n].num_resources =
+                       ARRAY_SIZE(watchdog_ipc_resources);
+               pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+               pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+               n++;
        }

...and here you do mfd_add_devices() instead of this stuff.

Check how lpc_ich.c designed.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#13 lore raw

Re: [RFC v1 3/6] platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices

Hi Andy,

Quoted 20 lines · Andy Shevchenko
Quoted 17 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

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

drivers/platform/x86/intel_pmc_ipc.c +9 -3
+static int ipc_create_pmc_devices(struct platform_device *pdev)
  {
-       int ret;
-
-       /* If we have ACPI based watchdog use that instead */
+       u8 n = 0;
+       struct mfd_cell *pmc_mfd_cells;
+
+       pmc_mfd_cells = devm_kzalloc(&pdev->dev,
+                       (sizeof(*pmc_mfd_cells) * PMC_IPC_MAX_MFD_BLOCK),
+                       GFP_KERNEL);
+       if (!pmc_mfd_cells)
+               return -ENOMEM;
Quoted 8 lines

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 677

Quoted 7 lines
drivers/platform/x86/intel_pmc_ipc.c +7 -0
+
+       /* Create PUNIT IPC MFD cell */
+       pmc_mfd_cells[n].name = PUNIT_DEVICE_NAME;
+       pmc_mfd_cells[n].id = -1;
+       pmc_mfd_cells[n].num_resources = ARRAY_SIZE(punit_ipc_resources);
+       pmc_mfd_cells[n].resources = punit_ipc_resources;
+       pmc_mfd_cells[n].ignore_resource_conflicts = 1;

Please, use static variables instead of allocated on a heap.

I will follow the model used in lpc_ich.c. Will fix it in next version.

Quoted 16 lines
Quoted 6 lines
drivers/platform/x86/intel_pmc_ipc.c +4 -0
+       n++;
+
+       /* If we have ACPI based watchdog use that instead, othewise create
+        * a MFD cell for iTCO watchdog*/
         if (!acpi_has_watchdog()) {

drivers/platform/x86/intel_pmc_ipc.c static struct resource telemetry_res[] = { · line 692

Quoted 10 lines
drivers/platform/x86/intel_pmc_ipc.c +9 -0
+               pmc_mfd_cells[n].name = TCO_DEVICE_NAME;
+               pmc_mfd_cells[n].id = -1;
+               pmc_mfd_cells[n].platform_data = &tco_info;
+               pmc_mfd_cells[n].pdata_size = sizeof(tco_info);
+               pmc_mfd_cells[n].num_resources =
+                       ARRAY_SIZE(watchdog_ipc_resources);
+               pmc_mfd_cells[n].resources = watchdog_ipc_resources;
+               pmc_mfd_cells[n].ignore_resource_conflicts = 1;
+               n++;
         }
Quoted 3 lines

...and here you do mfd_add_devices() instead of this stuff.

Check how lpc_ich.c designed.

Will fix it in next version.

Quoted 1 line

-
Sathya

#14 lore raw

RFC 4/6 platform: x86: Add generic Intel IPC driver

In reply to [email protected]

From: Kuppuswamy Sathyanarayanan <[email protected]>

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

---
 arch/x86/include/asm/intel_ipc_dev.h | 148 ++++++++++++
 drivers/platform/x86/Kconfig         |   8 +
 drivers/platform/x86/Makefile        |   1 +
 drivers/platform/x86/intel_ipc_dev.c | 433 +++++++++++++++++++++++++++++++++++
 4 files changed, 590 insertions(+)
 create mode 100644 arch/x86/include/asm/intel_ipc_dev.h
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c
arch/x86/include/asm/intel_ipc_dev.h new file +148 -0
new file mode 100644
index 0000000..29b21fa
@@ -0,0 +1,148 @@
+/*
+ * intel_ipc_dev.h: IPC class device header file
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+
+/* IPC channel type */
+#define IPC_CHANNEL_IA_PMC			0
+#define IPC_CHANNEL_IA_PUNIT			1
+#define IPC_CHANNEL_PMC_PUNIT			2
+#define IPC_CHANNEL_MAX				3
+
+/* IPC return code */
+#define IPC_DEV_ERR_NONE			0
+#define IPC_DEV_ERR_CMD_NOT_SUPPORTED		1
+#define IPC_DEV_ERR_CMD_NOT_SERVICED		2
+#define IPC_DEV_ERR_UNABLE_TO_SERVICE		3
+#define IPC_DEV_ERR_CMD_INVALID			4
+#define IPC_DEV_ERR_CMD_FAILED			5
+#define IPC_DEV_ERR_EMSECURITY			6
+#define IPC_DEV_ERR_UNSIGNEDKERNEL		7
+
+/* IPC mode */
+#define IPC_DEV_MODE_IRQ			0
+#define IPC_DEV_MODE_POLLING			1
+
+/* IPC dev constants */
+#define IPC_DEV_CMD_LOOP_CNT			3000000
+#define IPC_DEV_CMD_TIMEOUT			3 * HZ
+#define IPC_DEV_DATA_BUFFER_SIZE		16
+
+struct intel_ipc_dev;
+
+/**
+ * struct intel_ipc_dev_cfg - IPC device config structure.
+ *
+ * IPC device drivers should provide following config options to
+ * register new IPC device.
+ *
+ * @base:       IPC device memory resource start address.
+ * @wrbuf_reg:  IPC device data write register address.
+ * @rbuf_reg:   IPC device data read register address.
+ * @sptr_reg:   IPC device source data pointer register address.
+ * @dptr_reg: 	IPC device destination data pointer register address.
+ * @status_reg: IPC command status register address.
+ * @cmd_reg: 	IPC command register address.
+ * @mode: 	IRQ/POLLING mode.
+ * @irq:      	IPC device IRQ number.
+ * @irqflags:   IPC device IRQ flags.
+ * @chan_type:  IPC device channel type(PMC/PUNIT).
+ * @msi:    	Enable/Disable MSI for IPC commands.
+ *
+ */
+struct intel_ipc_dev_cfg {
+	void __iomem *base;
+	void __iomem *wrbuf_reg;
+	void __iomem *rbuf_reg;
+	void __iomem *sptr_reg;
+	void __iomem *dptr_reg;
+	void __iomem *status_reg;
+	void __iomem *cmd_reg;
+	int mode;
+	int irq;
+	int irqflags;
+	int chan_type;
+	bool use_msi;
+};
+
+/**
+ * struct intel_ipc_dev_ops - IPC device ops structure.
+ *
+ * Call backs for IPC device specific operations.
+ *
+ * @err_code	: Status to error code conversion function.
+ * @busy_check	: Check for IPC busy status.
+ * @cmd_msi	: Enable MSI for IPC commands.
+ *
+ */
+struct intel_ipc_dev_ops {
+	int (*to_err_code)(int status);
+	int (*busy_check)(int status);
+	u32 (*enable_msi)(u32 cmd);
+
+};
+
+/**
+ * struct intel_ipc_dev - Intel IPC device structure.
+ *
+ * Used with devm_intel_ipc_dev_create() to create new IPC device.
+ *
+ * @dev:        	IPC device object.
+ * @cmd:  		Current IPC device command.
+ * @cmd_complete:   	Command completion object.
+ * @lock:   		Lock to protect IPC device structure.
+ * @ops: 		IPC device ops pointer.
+ * @cfg: 		IPC device cfg pointer.
+ *
+ */
+struct intel_ipc_dev {
+	struct device dev;
+	int cmd;
+	struct completion cmd_complete;
+	struct mutex lock;
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+};
+
+#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)
+
+/* API to create new IPC device */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops);
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd);
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd, u8 *in,
+		u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);
+#else
+
+static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(
+		struct device *dev,
+		const char *devname, struct intel_ipc_dev_cfg *cfg,
+		struct intel_ipc_dev_ops *ops)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)
+{
+	return -EINVAL;
+}
+
+static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd,
+		u8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr)
+{
+	return -EINVAL;
+}
+
+#endif
drivers/platform/x86/Kconfig +8 -0
index b048607..030eac7 100644
@@ -1138,6 +1138,14 @@ config SILEAD_DMI
 	  with the OS-image for the device. This option supplies the missing
 	  information. Enable this for x86 tablets with Silead touchscreens.
 
+config INTEL_IPC_DEV
+	tristate "Intel IPC Device Driver"
+	depends on X86_64
+	---help---
+	  This driver implements core features of Intel IPC device. Devices
+	  like PMC, SCU, PUNIT, etc can use interfaces provided by this
+	  driver to implement IPC protocol of their respective device.
+
 endif # X86_PLATFORM_DEVICES
 
 config PMC_ATOM
drivers/platform/x86/Makefile +1 -0
index 91cec17..04e11ce 100644
@@ -83,3 +83,4 @@ obj-$(CONFIG_PMC_ATOM)		+= pmc_atom.o
 obj-$(CONFIG_MLX_PLATFORM)	+= mlx-platform.o
 obj-$(CONFIG_MLX_CPLD_PLATFORM)	+= mlxcpld-hotplug.o
 obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
+obj-$(CONFIG_INTEL_IPC_DEV)	+= intel_ipc_dev.o
drivers/platform/x86/intel_ipc_dev.c new file +433 -0

433 lines not on this page · raw message

Signature · 1 line
2.7.4
#15 lore raw

Re: [RFC v1 4/6] platform: x86: Add generic Intel IPC driver

Quoted 10 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

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

---
 arch/x86/include/asm/intel_ipc_dev.h | 148 ++++++++++++

No, it should go under include/linux/platform_data/x86/

Quoted 2 lines

+/*
+ * intel_ipc_dev.h: IPC class device header file

No file names in the top of files.

Quoted 10 lines

+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */

arch/x86/include/asm/intel_ipc_dev.h line 70

Quoted 8 lines
arch/x86/include/asm/intel_ipc_dev.h new file +8 -0
+struct intel_ipc_dev_cfg {
+       void __iomem *base;
+       void __iomem *wrbuf_reg;
+       void __iomem *rbuf_reg;
+       void __iomem *sptr_reg;
+       void __iomem *dptr_reg;
+       void __iomem *status_reg;
+       void __iomem *cmd_reg;

No, you have to switch to regmap instead.

Quoted 6 lines
arch/x86/include/asm/intel_ipc_dev.h new file +6 -0
+       int mode;
+       int irq;
+       int irqflags;
+       int chan_type;
+       bool use_msi;
+};
Signature · 2 lines
With Best Regards,
Andy Shevchenko
#16 lore raw

Re: [RFC v1 4/6] platform: x86: Add generic Intel IPC driver

Hi Andy,

Quoted 35 lines · Andy Shevchenko
Quoted 10 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

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

---
  arch/x86/include/asm/intel_ipc_dev.h | 148 ++++++++++++

No, it should go under include/linux/platform_data/x86/

Quoted 2 lines

+/*
+ * intel_ipc_dev.h: IPC class device header file

No file names in the top of files.

Quoted 9 lines

+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */

arch/x86/include/asm/intel_ipc_dev.h line 70

Quoted 8 lines
arch/x86/include/asm/intel_ipc_dev.h new file +8 -0
+struct intel_ipc_dev_cfg {
+       void __iomem *base;
+       void __iomem *wrbuf_reg;
+       void __iomem *rbuf_reg;
+       void __iomem *sptr_reg;
+       void __iomem *dptr_reg;
+       void __iomem *status_reg;
+       void __iomem *cmd_reg;
Quoted 1 line

No, you have to switch to regmap instead.

Do you want me to register regmap in intel_pmc_ipc.c and pass this regmap pointer to devm_intel_ipc_dev_create() instead of regular mem address ? Please correct me if my understanding is incorrect.

But I don't understand how this change will improve the design.

Quoted 8 lines

arch/x86/include/asm/intel_ipc_dev.h line 76

Quoted 7 lines
arch/x86/include/asm/intel_ipc_dev.h new file +6 -0
+       int mode;
+       int irq;
+       int irqflags;
+       int chan_type;
+       bool use_msi;
+};

-
Sathya

#17 lore raw

Re: [RFC v1 4/6] platform: x86: Add generic Intel IPC driver

Quoted 13 lines · sathya
Quoted 12 lines · Andy Shevchenko
Quoted 8 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…
arch/x86/include/asm/intel_ipc_dev.h new file +8 -0
+struct intel_ipc_dev_cfg {
+       void __iomem *base;
+       void __iomem *wrbuf_reg;
+       void __iomem *rbuf_reg;
+       void __iomem *sptr_reg;
+       void __iomem *dptr_reg;
+       void __iomem *status_reg;
+       void __iomem *cmd_reg;
Quoted 8 lines
Quoted 1 line

No, you have to switch to regmap instead.

Do you want me to register regmap in intel_pmc_ipc.c and pass this regmap pointer to devm_intel_ipc_dev_create()
instead of regular mem address ? Please correct me if my understanding is incorrect.

But I don't understand how this change will improve the design.

We will have a core part which takes a PMC/SCU regmap on input.

Core part will not know about exact PMC in use. On top of core part you will have one driver per PMC type.

In exchange core part will provide a generic set of functions like "send simple command", "send command", "do ...smth...".

Core part _is_ a library.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
arch/x86/include/asm/intel_ipc_dev.h new file +6 -0
+       int mode;
+       int irq;
+       int irqflags;
+       int chan_type;
+       bool use_msi;
+};
Signature · 2 lines
With Best Regards,
Andy Shevchenko
#18 lore raw

RFC 5/6 platform/x86: intel_punit_ipc: Use generic intel ipc device calls

In reply to [email protected]

From: Kuppuswamy Sathyanarayanan <[email protected]>

Removed redundant IPC helper functions and refactored the intel_punit_ipc_simple_command() and intel_punit_ipc_command() functions to use eneric IPC device driver APIs.

---
 drivers/platform/x86/intel_punit_ipc.c | 234 ++++++++++++---------------------
 1 file changed, 84 insertions(+), 150 deletions(-)
drivers/platform/x86/intel_punit_ipc.c +84 -150
index a47a41f..d113987 100644
@@ -19,18 +19,22 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <asm/intel_punit_ipc.h>
+#include <asm/intel_ipc_dev.h>
 
 /* IPC Mailbox registers */
 #define OFFSET_DATA_LOW		0x0
 #define OFFSET_DATA_HIGH	0x4
 /* bit field of interface register */
 #define	CMD_RUN			BIT(31)
-#define	CMD_ERRCODE_MASK	GENMASK(7, 0)
 #define	CMD_PARA1_SHIFT		8
 #define	CMD_PARA2_SHIFT		16
 
 #define CMD_TIMEOUT_SECONDS	1
 
+/* IPC PUNIT commands */
+#define	IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK	GENMASK(7, 0)
+#define	IPC_DEV_PUNIT_CMD_STATUS_BUSY		BIT(31)
+
 enum {
 	BASE_DATA = 0,
 	BASE_IFACE,
@@ -39,97 +43,19 @@ enum {
 
 typedef struct {
 	struct device *dev;
-	struct mutex lock;
-	int irq;
-	struct completion cmd_complete;
 	/* base of interface and data registers */
 	void __iomem *base[RESERVED_IPC][BASE_MAX];
+	struct intel_ipc_dev *ipc_dev[RESERVED_IPC];
 	IPC_TYPE type;
 } IPC_DEV;
 
 static IPC_DEV *punit_ipcdev;
 
-static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd)
-{
-	writel(cmd, ipcdev->base[type][BASE_IFACE]);
-}
-
-static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
-
-static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
-}
-
-static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
-{
-	writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
-}
-
-static const char *ipc_err_string(int error)
-{
-	if (error == IPC_PUNIT_ERR_SUCCESS)
-		return "no error";
-	else if (error == IPC_PUNIT_ERR_INVALID_CMD)
-		return "invalid command";
-	else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
-		return "invalid parameter";
-	else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
-		return "command timeout";
-	else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
-		return "command locked";
-	else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
-		return "invalid vr id";
-	else if (error == IPC_PUNIT_ERR_VR_ERR)
-		return "vr error";
-	else
-		return "unknown error";
-}
-
-static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
-{
-	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
-	int errcode;
-	int status;
-
-	if (ipcdev->irq) {
-		if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
-						 CMD_TIMEOUT_SECONDS * HZ)) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	} else {
-		while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --loops)
-			udelay(1);
-		if (!loops) {
-			dev_err(ipcdev->dev, "IPC timed out\n");
-			return -ETIMEDOUT;
-		}
-	}
-
-	status = ipc_read_status(ipcdev, type);
-	errcode = status & CMD_ERRCODE_MASK;
-	if (errcode) {
-		dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
-			ipc_err_string(errcode), status);
-		return -EIO;
-	}
-
-	return 0;
-}
+const char *ipc_dev_name[RESERVED_IPC] = {
+	"punit_bios_ipc",
+	"punit_gtd_ipc",
+	"punit_isp_ipc"
+};
 
 /**
  * intel_punit_ipc_simple_command() - Simple IPC command
@@ -146,21 +72,13 @@ int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
 	IPC_DEV *ipcdev = punit_ipcdev;
 	IPC_TYPE type;
 	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
 
-	reinit_completion(&ipcdev->cmd_complete);
 	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
 
 	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
 	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-	ret = intel_punit_ipc_check_status(ipcdev, type);
 
-	mutex_unlock(&ipcdev->lock);
-
-	return ret;
+	return ipc_dev_simple_cmd(ipcdev->ipc_dev[type], val);
 }
 EXPORT_SYMBOL(intel_punit_ipc_simple_command);
 
@@ -180,48 +98,22 @@ int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
 {
 	IPC_DEV *ipcdev = punit_ipcdev;
 	IPC_TYPE type;
-	u32 val;
-	int ret;
-
-	mutex_lock(&ipcdev->lock);
+	u32 val, len;
 
-	reinit_completion(&ipcdev->cmd_complete);
 	type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
-	if (in) {
-		ipc_write_data_low(ipcdev, type, *in);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			ipc_write_data_high(ipcdev, type, *++in);
-	}
+	if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
+		len = 2;
+	else
+		len = 1;
 
 	val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
 	val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
-	ipc_write_cmd(ipcdev, type, val);
-
-	ret = intel_punit_ipc_check_status(ipcdev, type);
-	if (ret)
-		goto out;
-
-	if (out) {
-		*out = ipc_read_data_low(ipcdev, type);
-		if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
-			*++out = ipc_read_data_high(ipcdev, type);
-	}
 
-out:
-	mutex_unlock(&ipcdev->lock);
-	return ret;
+	return ipc_dev_raw_cmd(ipcdev->ipc_dev[type], val, (u8*)in, len * 4,
+			out, len, 0, 0);
 }
 EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
 
-static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
-{
-	IPC_DEV *ipcdev = dev_id;
-
-	complete(&ipcdev->cmd_complete);
-	return IRQ_HANDLED;
-}
-
 static int intel_punit_get_bars(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -282,9 +174,57 @@ static int intel_punit_get_bars(struct platform_device *pdev)
 	return 0;
 }
 
+static int punit_ipc_err_code(int status)
+{
+	return (status & IPC_DEV_PUNIT_CMD_STATUS_ERR_MASK);
+}
+
+static int punit_ipc_busy_check(int status)
+{
+	return status | IPC_DEV_PUNIT_CMD_STATUS_BUSY;
+}
+
+static struct intel_ipc_dev *intel_punit_ipc_dev_create(struct device *dev,
+		const char *devname,
+		int irq,
+		void __iomem *base,
+		void __iomem *data)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+
+        cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+        if (!cfg)
+                return ERR_PTR(-ENOMEM);
+
+	ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+	/* set IPC dev ops */
+	ops->to_err_code = punit_ipc_err_code;
+	ops->busy_check = punit_ipc_busy_check;
+
+	if (irq > 0)
+	        cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+	        cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PUNIT;
+	cfg->irq = irq;
+	cfg->irqflags = IRQF_NO_SUSPEND | IRQF_SHARED;
+	cfg->base = base;
+	cfg->wrbuf_reg = data;
+	cfg->rbuf_reg = data;
+	cfg->status_reg = base;
+	cfg->cmd_reg = base;
+
+	return devm_intel_ipc_dev_create(dev, devname, cfg, ops);
+}
+
 static int intel_punit_ipc_probe(struct platform_device *pdev)
 {
-	int irq, ret;
+	int irq, ret, i;
 
 	punit_ipcdev = devm_kzalloc(&pdev->dev,
 				    sizeof(*punit_ipcdev), GFP_KERNEL);
@@ -294,35 +234,30 @@ static int intel_punit_ipc_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, punit_ipcdev);
 
 	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
-		punit_ipcdev->irq = 0;
-		dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
-	} else {
-		ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
-				       IRQF_NO_SUSPEND, "intel_punit_ipc",
-				       &punit_ipcdev);
-		if (ret) {
-			dev_err(&pdev->dev, "Failed to request irq: %d\n", irq);
-			return ret;
-		}
-		punit_ipcdev->irq = irq;
-	}
 
 	ret = intel_punit_get_bars(pdev);
 	if (ret)
-		goto out;
+		return ret;
+
+	for (i = 0; i < RESERVED_IPC; i++) {
+		punit_ipcdev->ipc_dev[i] = intel_punit_ipc_dev_create(
+				&pdev->dev,
+				ipc_dev_name[i],
+				irq,
+				punit_ipcdev->base[i][BASE_IFACE],
+				punit_ipcdev->base[i][BASE_DATA]);
+
+		if (IS_ERR(punit_ipcdev->ipc_dev[i])) {
+			dev_err(&pdev->dev, "%s create failed\n",
+					ipc_dev_name[i]);
+			return PTR_ERR(punit_ipcdev->ipc_dev[i]);
+		}
+	}
 
 	punit_ipcdev->dev = &pdev->dev;
-	mutex_init(&punit_ipcdev->lock);
-	init_completion(&punit_ipcdev->cmd_complete);
 
-out:
 	return ret;
-}
 
-static int intel_punit_ipc_remove(struct platform_device *pdev)
-{
-	return 0;
 }
 
 static const struct acpi_device_id punit_ipc_acpi_ids[] = {
@@ -332,7 +267,6 @@ static const struct acpi_device_id punit_ipc_acpi_ids[] = {
 
 static struct platform_driver intel_punit_ipc_driver = {
 	.probe = intel_punit_ipc_probe,
-	.remove = intel_punit_ipc_remove,
 	.driver = {
 		.name = "intel_punit_ipc",
 		.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids),
Signature · 1 line
2.7.4
#19 lore raw

RFC 6/6 platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls

In reply to [email protected]

From: Kuppuswamy Sathyanarayanan <[email protected]>

Removed redundant IPC helper functions and refactored the intel_pmc_ipc_simple_command() and intel_pmc_ipc_command() functions to use generic IPC device driver APIs.

---
 drivers/platform/x86/intel_pmc_ipc.c | 302 ++++++++++++-----------------------
 1 file changed, 103 insertions(+), 199 deletions(-)
drivers/platform/x86/intel_pmc_ipc.c +103 -199
index 43533ec..675f8d9 100644
@@ -36,6 +36,7 @@
 #include <linux/mfd/core.h>
 
 #include <asm/intel_pmc_ipc.h>
+#include <asm/intel_ipc_dev.h>
 
 #include <linux/platform_data/itco_wdt.h>
 
@@ -45,18 +46,8 @@
  * The ARC handles the interrupt and services it, writing optional data to
  * the IPC1 registers, updates the IPC_STS response register with the status.
  */
-#define IPC_CMD			0x0
-#define		IPC_CMD_MSI		0x100
 #define		IPC_CMD_SIZE		16
 #define		IPC_CMD_SUBCMD		12
-#define IPC_STATUS		0x04
-#define		IPC_STATUS_IRQ		0x4
-#define		IPC_STATUS_ERR		0x2
-#define		IPC_STATUS_BUSY		0x1
-#define IPC_SPTR		0x08
-#define IPC_DPTR		0x0C
-#define IPC_WRITE_BUFFER	0x80
-#define IPC_READ_BUFFER		0x90
 
 /* Residency with clock rate at 19.2MHz to usecs */
 #define S0IX_RESIDENCY_IN_USECS(d, s)		\
@@ -71,11 +62,6 @@
  */
 #define IPC_DATA_BUFFER_SIZE	16
 
-#define IPC_LOOP_CNT		3000000
-#define IPC_MAX_SEC		3
-
-#define IPC_TRIGGER_MODE_IRQ		true
-
 /* exported resources from IFWI */
 #define PLAT_RESOURCE_IPC_INDEX		0
 #define PLAT_RESOURCE_IPC_SIZE		0x1000
@@ -121,8 +107,28 @@ enum {
 	PMC_IPC_MAX_MFD_BLOCK
 };
 
+/* IPC PMC commands */
+#define	IPC_DEV_PMC_CMD_MSI			BIT(8)
+#define	IPC_DEV_PMC_CMD_SIZE			16
+#define	IPC_DEV_PMC_CMD_SUBCMD			12
+#define	IPC_DEV_PMC_CMD_STATUS			BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_IRQ		BIT(2)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR		BIT(1)
+#define	IPC_DEV_PMC_CMD_STATUS_ERR_MASK		GENMASK(7, 0)
+#define	IPC_DEV_PMC_CMD_STATUS_BUSY		BIT(0)
+
+/*IPC PMC reg offsets */
+#define IPC_DEV_PMC_STATUS_OFFSET		0x04
+#define IPC_DEV_PMC_SPTR_OFFSET			0x08
+#define IPC_DEV_PMC_DPTR_OFFSET			0x0C
+#define IPC_DEV_PMC_WRBUF_OFFSET		0x80
+#define IPC_DEV_PMC_RBUF_OFFSET			0x90
+
 static struct intel_pmc_ipc_dev {
 	struct device *dev;
+	struct intel_ipc_dev *pmc_ipc_dev;
+	struct intel_ipc_dev_ops ops;
+	struct intel_ipc_dev_cfg cfg;
 	void __iomem *ipc_base;
 	bool irq_mode;
 	int irq;
@@ -137,58 +143,9 @@ static struct intel_pmc_ipc_dev {
 	u8 telem_res_inval;
 } 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 u8 __maybe_unused ipc_data_readb(u32 offset)
-{
-	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-static inline u32 ipc_data_readl(u32 offset)
-{
-	return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
 static inline u64 gcr_data_readq(u32 offset)
 {
 	return readq(ipcdev.gcr_mem_base + offset);
@@ -315,52 +272,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.
@@ -373,102 +284,95 @@ static int intel_pmc_ipc_check_status(void)
  */
 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;
+	return ipc_dev_simple_cmd(ipcdev.pmc_ipc_dev,
+			sub << IPC_CMD_SUBCMD | cmd);
 }
 EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
 
+
 /**
- * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
+ * 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.
- * @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.
+ * Send an IPC command to PMC with input/output data.
  *
  * Return:	an IPC error code or 0 on success.
  */
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
-			  u32 outlen, u32 dptr, u32 sptr)
+int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
+			  u32 *out, u32 outlen)
 {
-	u32 wbuf[4] = { 0 };
-	int ret;
-	int i;
+	cmd = (inlen << IPC_CMD_SIZE) | (sub << IPC_CMD_SUBCMD) | cmd;
 
 	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 ipc_dev_raw_cmd(ipcdev.pmc_ipc_dev, cmd, in, inlen, out,
+			outlen, 0, 0);
 
-	return ret;
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
+EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
 
-/**
- * intel_pmc_ipc_command() -  IPC command with input/output data
- * @cmd:	IPC command code.
- * @sub:	IPC command sub type.
- * @in:		input data of this IPC command.
- * @inlen:	input data length in bytes.
- * @out:	output data of this IPC command.
- * @outlen:	output data length in dwords.
- *
- * Send an IPC command to PMC with input/output data.
- *
- * Return:	an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-			  u32 *out, u32 outlen)
+static int pmc_ipc_err_code(int status)
 {
-	return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+	return ((status >> IPC_DEV_PMC_CMD_SIZE) &
+			IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
 }
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
 
-static irqreturn_t ioc(int irq, void *dev_id)
+static int pmc_ipc_busy_check(int status)
 {
-	int status;
+	return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
+}
 
-	if (ipcdev.irq_mode) {
-		status = ipc_read_status();
-		writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
-	}
-	complete(&ipcdev.cmd_complete);
+static u32 pmc_ipc_enable_msi(u32 cmd)
+{
+	return cmd | IPC_DEV_PMC_CMD_MSI;
+}
+
+static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
+		struct device *pmc_dev,
+		void __iomem *base,
+		int irq)
+{
+	struct intel_ipc_dev_ops *ops;
+	struct intel_ipc_dev_cfg *cfg;
+
+	cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return ERR_PTR(-ENOMEM);
 
-	return IRQ_HANDLED;
+	ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return ERR_PTR(-ENOMEM);
+
+	/* set IPC dev ops */
+	ops->to_err_code = pmc_ipc_err_code;
+	ops->busy_check = pmc_ipc_busy_check;
+	ops->enable_msi = pmc_ipc_enable_msi;
+
+	/* set cfg options */
+	if (irq > 0)
+		cfg->mode = IPC_DEV_MODE_IRQ;
+	else
+		cfg->mode = IPC_DEV_MODE_POLLING;
+
+	cfg->chan_type = IPC_CHANNEL_IA_PMC;
+	cfg->irq = irq;
+	cfg->use_msi = true;
+	cfg->base = base;
+	cfg->wrbuf_reg = cfg->base + IPC_DEV_PMC_WRBUF_OFFSET;
+	cfg->rbuf_reg = cfg->base + IPC_DEV_PMC_RBUF_OFFSET;
+	cfg->sptr_reg = cfg->base + IPC_DEV_PMC_SPTR_OFFSET;
+	cfg->dptr_reg = cfg->base + IPC_DEV_PMC_DPTR_OFFSET;
+	cfg->status_reg = cfg->base + IPC_DEV_PMC_STATUS_OFFSET;
+	cfg->cmd_reg = cfg->base;
+
+	return devm_intel_ipc_dev_create(pmc_dev, "intel_pmc_ipc",
+			cfg, ops);
 }
 
 static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -478,7 +382,8 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	int len;
 
 	ipcdev.dev = &pci_dev_get(pdev)->dev;
-	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
+
+	ipcdev.dev = &pdev->dev;
 
 	ret = pci_enable_device(pdev);
 	if (ret)
@@ -496,15 +401,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		goto free_pci_resources;
 	}
 
-	init_completion(&ipcdev.cmd_complete);
-
-	if (devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
-				&ipcdev)) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		ret = -EBUSY;
-		goto free_pci_resources;
-	}
-
 	ipcdev.ipc_base = devm_ioremap_nocache(&pdev->dev, pci_resource, len);
 	if (!ipcdev.ipc_base) {
 		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
@@ -512,6 +408,15 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		goto free_pci_resources;
 	}
 
+	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			ipcdev.ipc_base, pdev->irq);
+	if (IS_ERR(ipcdev.pmc_ipc_dev)) {
+		dev_err(ipcdev.dev,
+				"Failed to create PMC IPC device\n");
+		ret = PTR_ERR(ipcdev.pmc_ipc_dev);
+		goto free_pci_resources;
+	}
+
 	return 0;
 
 free_pci_resources:
@@ -841,14 +746,12 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
 
 static int ipc_plat_probe(struct platform_device *pdev)
 {
-	int ret;
+	int ret, irq;
 
 	ipcdev.dev = &pdev->dev;
-	ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
-	init_completion(&ipcdev.cmd_complete);
 
-	ipcdev.irq = platform_get_irq(pdev, 0);
-	if (ipcdev.irq < 0) {
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
 		dev_err(&pdev->dev, "Failed to get irq\n");
 		return -EINVAL;
 	}
@@ -865,12 +768,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
-			     "intel_pmc_ipc", &ipcdev)) {
-		dev_err(&pdev->dev, "Failed to request irq\n");
-		return -EBUSY;
-	}
-
 	ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
@@ -878,6 +775,13 @@ static int ipc_plat_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+			ipcdev.ipc_base, irq);
+	if (IS_ERR(ipcdev.pmc_ipc_dev)) {
+		dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
+		return PTR_ERR(ipcdev.pmc_ipc_dev);
+	}
+
 	ipcdev.has_gcr_regs = true;
 
 	return 0;
Signature · 1 line
2.7.4
#20 lore raw
In reply to [email protected]
Quoted 8 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

From: Kuppuswamy Sathyanarayanan <[email protected]>

Hi All,

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

Quoted 9 lines

For now, Since we don't have any good test platform for SCU, I am not planning to modify intel_scu_ipc.c.

This patch-set addresses following issues(except #4) in intel_pmc_ipc and intel_punit_ipc drivers.

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

Thanks for doing it, really appreciated!
I'm on vacation for few days now, I will definitely check this after.

Quoted 61 lines

Patch #1, #2 fixes the issue #1.

Patch #3 fixes the issue #2.

Patch #4, #5, #6 fixes the issue #3.

To fix issue #4 we might need to make changes to drivers that use IPC APIs. So I am not sure whether its worth the effort. Maintainers, let me know your inputs.

If we have to address it, then we might need to adapt to model similar to extcon framework.

ipc_dev = intel_ipc_get_dev("intel_pmc_ipc");

PMC IPC call would look like,

intel_pmc_ipc_command(ipc_dev, cmd, sub, in, inlen, out, outlen)

More info on adapted solution (for issue #1):
----------------------------------------------

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

API for creating a new IPC channel device is,

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

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

For example, after using this new model, PMC ipc comand send routine will look like,

int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out, u32 outlen)

{
    // this function is implemented in generic Intel IPC class driver.
    return ipc_dev_raw_cmd(ipcdev.pmc_ipc_dev, f(cmd, sub), in, inlen, out, outlen, 0, 0);
}

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

Kuppuswamy Sathyanarayanan (6):
platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe() platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe platform/x86: intel_pmc_ipc: Use MFD framework to create dependent
devices
platform: x86: Add generic Intel IPC driver platform/x86: intel_punit_ipc: Use generic intel ipc device calls platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls

 arch/x86/include/asm/intel_ipc_dev.h   | 148 +++++++
 drivers/platform/x86/Kconfig           |   8 +
 drivers/platform/x86/Makefile          |   1 +
 drivers/platform/x86/intel_ipc_dev.c   | 433 ++++++++++++++++++++
 drivers/platform/x86/intel_pmc_ipc.c   | 715 ++++++++++++---------------------
 drivers/platform/x86/intel_punit_ipc.c | 234 ++++-------
 6 files changed, 930 insertions(+), 609 deletions(-)
 create mode 100644 arch/x86/include/asm/intel_ipc_dev.h
 create mode 100644 drivers/platform/x86/intel_ipc_dev.c

--
2.7.4

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#21 lore raw
In reply to [email protected]
Quoted 5 lines · On Tue, Aug 1, 2017 at 9:13 PM, <[email protected]>…

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

For now, Since we don't have any good test platform for SCU, I am not planning to modify intel_scu_ipc.c.

It should be done as well, so, we might split it into logical parts, see below.

Quoted 16 lines

This patch-set addresses following issues(except #4) in intel_pmc_ipc and intel_punit_ipc drivers.

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

Patch #1, #2 fixes the issue #1.

Patch #3 fixes the issue #2.

Patch #4, #5, #6 fixes the issue #3.

To fix issue #4 we might need to make changes to drivers that use IPC APIs. So I am not sure whether its worth the effort. Maintainers, let me know your inputs.

Quoted 7 lines

If we have to address it, then we might need to adapt to model similar to extcon framework.

ipc_dev = intel_ipc_get_dev("intel_pmc_ipc");

PMC IPC call would look like,

intel_pmc_ipc_command(ipc_dev, cmd, sub, in, inlen, out, outlen)

Yep, where ipc_dev is an opaque pointer.

Quoted 13 lines

More info on adapted solution (for issue #1):
----------------------------------------------

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

API for creating a new IPC channel device is,

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

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

For example, after using this new model, PMC ipc comand send routine will look like,

Quoted 7 lines

int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out, u32 outlen)

{
    // this function is implemented in generic Intel IPC class driver.
    return ipc_dev_raw_cmd(ipcdev.pmc_ipc_dev, f(cmd, sub), in, inlen, out, outlen, 0, 0);
}

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

So, for me the steps of refactoring would look like following:
1) create an IPC library with regmap API and devres framework in use;
2) convert users one-by-one;
3) remove old leftovers and APIs.

Signature · 2 lines
With Best Regards,
Andy Shevchenko