Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1701317 > unrolled thread
| Started by | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| First post | 2017-08-01 20:20 +0200 |
| Last post | 2017-08-06 20:40 +0200 |
| Articles | 7 — 2 participants |
Back to article view | Back to linux.kernel
[RFC v1 0/6] PMC/PUNIT IPC driver cleanup sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
[RFC v1 1/6] platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe() sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
[RFC v1 4/6] platform: x86: Add generic Intel IPC driver sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
[RFC v1 2/6] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
[RFC v1 6/6] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
[RFC v1 5/6] platform/x86: intel_punit_ipc: Use generic intel ipc device calls sathyanarayanan.kuppuswamy@linux.intel.com - 2017-08-01 20:20 +0200
Re: [RFC v1 0/6] PMC/PUNIT IPC driver cleanup Andy Shevchenko <andy.shevchenko@gmail.com> - 2017-08-06 20:40 +0200
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 0/6] PMC/PUNIT IPC driver cleanup |
| Message-ID | <u9JVT-7qE-7@gated-at.bofh.it> |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Hi All,
Currently intel_pmc_ipc.c, intel_punit_ipc.c, intel_scu_ipc.c drivers implements the same IPC features.
This code duplication could be avoided if we implement the IPC driver as a 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
--
2.7.4
[toc] | [next] | [standalone]
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 1/6] platform/x86: intel_pmc_ipc: Fix error handling in ipc_pci_probe() |
| Message-ID | <u9JVU-7qE-33@gated-at.bofh.it> |
| In reply to | #1701317 |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
This patch adds proper error handling for failure cases in
ipc_pci_probe() function.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
drivers/platform/x86/intel_pmc_ipc.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index bb792a5..7b65237 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -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;
}
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 4/6] platform: x86: Add generic Intel IPC driver |
| Message-ID | <u9JVU-7qE-29@gated-at.bofh.it> |
| In reply to | #1701317 |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Currently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c
redundantly implements the same IPC features and has lot of code
duplication between them. This driver addresses this issue by grouping
the common IPC functionalities under the same driver.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
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
diff --git a/arch/x86/include/asm/intel_ipc_dev.h b/arch/x86/include/asm/intel_ipc_dev.h
new file mode 100644
index 0000000..29b21fa
--- /dev/null
+++ b/arch/x86/include/asm/intel_ipc_dev.h
@@ -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
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b048607..030eac7 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -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
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 91cec17..04e11ce 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -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
diff --git a/drivers/platform/x86/intel_ipc_dev.c b/drivers/platform/x86/intel_ipc_dev.c
new file mode 100644
index 0000000..6c86b62
--- /dev/null
+++ b/drivers/platform/x86/intel_ipc_dev.c
@@ -0,0 +1,433 @@
+/*
+ * intel_ipc_dev.c: Intel IPC device class driver
+ *
+ * (C) Copyright 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/intel_ipc_dev.h>
+
+/* mutex to sync different ipc devices in same channel */
+static struct mutex channel_lock[IPC_CHANNEL_MAX];
+
+static void ipc_channel_lock_init(void)
+{
+ int i;
+
+ for (i = 0; i < IPC_CHANNEL_MAX; i++)
+ mutex_init(&channel_lock[i]);
+}
+
+static struct class intel_ipc_class = {
+ .name = "intel_ipc",
+ .owner = THIS_MODULE,
+};
+
+static int __init intel_ipc_init(void)
+{
+ ipc_channel_lock_init();
+ return class_register(&intel_ipc_class);
+}
+
+static void __exit intel_ipc_exit(void)
+{
+ class_unregister(&intel_ipc_class);
+}
+
+static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev)
+{
+ int chan_type = ipc_dev->cfg->chan_type;
+
+ if (!ipc_dev)
+ return -ENODEV;
+
+ if (chan_type > IPC_CHANNEL_MAX)
+ return -EINVAL;
+
+ /* acquire channel lock */
+ mutex_lock(&channel_lock[chan_type]);
+
+ /* acquire IPC device lock */
+ mutex_lock(&ipc_dev->lock);
+
+ return 0;
+}
+
+static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev)
+{
+ int chan_type = ipc_dev->cfg->chan_type;
+
+ if (!ipc_dev)
+ return -ENODEV;
+
+ if (chan_type > IPC_CHANNEL_MAX)
+ return -EINVAL;
+
+ /* release IPC device lock */
+ mutex_unlock(&ipc_dev->lock);
+
+ /* release channel lock */
+ mutex_unlock(&channel_lock[chan_type]);
+
+ return 0;
+}
+
+
+static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,
+ int error)
+{
+ switch (error) {
+ case IPC_DEV_ERR_NONE:
+ return "No error";
+ case IPC_DEV_ERR_CMD_NOT_SUPPORTED:
+ return "Command not-supported/Invalid";
+ case IPC_DEV_ERR_CMD_NOT_SERVICED:
+ return "Command not-serviced/Invalid param";
+ case IPC_DEV_ERR_UNABLE_TO_SERVICE:
+ return "Unable-to-service/Cmd-timeout";
+ case IPC_DEV_ERR_CMD_INVALID:
+ return "Command-invalid/Cmd-locked";
+ case IPC_DEV_ERR_CMD_FAILED:
+ return "Command-failed/Invalid-VR-id";
+ case IPC_DEV_ERR_EMSECURITY:
+ return "Invalid Battery/VR-Error";
+ case IPC_DEV_ERR_UNSIGNEDKERNEL:
+ return "Unsigned kernel";
+ default:
+ return "Unknown Command";
+ };
+}
+
+/* Helper function to read IPC device status register */
+static inline u32 ipc_dev_read_status(struct intel_ipc_dev *ipc_dev)
+{
+ return readl(ipc_dev->cfg->status_reg);
+}
+
+/* Helper function to write 32 bits to IPC device data register */
+static inline void ipc_dev_write_datal(struct intel_ipc_dev *ipc_dev,
+ u32 data, u32 offset)
+{
+ writel(data, ipc_dev->cfg->wrbuf_reg + offset);
+}
+
+/* Helper function to read 32 bits from IPC device data register */
+static inline u32 ipc_dev_read_datal(struct intel_ipc_dev *ipc_dev,
+ u32 offset)
+{
+ return readl(ipc_dev->cfg->rbuf_reg + offset);
+}
+
+/* Helper function to send given command to IPC device */
+static inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev,
+ u32 cmd)
+{
+ ipc_dev->cmd = cmd;
+
+ if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)
+ reinit_completion(&ipc_dev->cmd_complete);
+
+ if (ipc_dev->ops->enable_msi)
+ cmd = ipc_dev->ops->enable_msi(cmd);
+
+ writel(cmd, ipc_dev->cfg->cmd_reg);
+}
+
+static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev)
+{
+ int status = ipc_dev_read_status(ipc_dev);
+
+ if (ipc_dev->ops->busy_check)
+ return ipc_dev->ops->busy_check(status);
+
+ return 0;
+}
+
+/* Check the status of IPC command and return err code if failed */
+static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev)
+{
+ int loop_count = IPC_DEV_CMD_LOOP_CNT;
+ int status;
+ int ret = 0;
+
+ if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+ if (!wait_for_completion_timeout(&ipc_dev->cmd_complete,
+ IPC_DEV_CMD_TIMEOUT))
+ ret = -ETIMEDOUT;
+ } else {
+ while (ipc_dev_status_busy(ipc_dev) && --loop_count)
+ udelay(1);
+ if (!loop_count)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret < 0) {
+ dev_err(&ipc_dev->dev,
+ "IPC timed out, CMD=0x%x\n", ipc_dev->cmd);
+ return ret;
+ }
+
+ status = ipc_dev_read_status(ipc_dev);
+
+ if (ipc_dev->ops->to_err_code)
+ ret = ipc_dev->ops->to_err_code(status);
+
+ if (ret) {
+ dev_err(&ipc_dev->dev,
+ "IPC failed: %s, STS=0x%x, CMD=0x%x\n",
+ ipc_dev_err_string(ipc_dev, ret),
+ status, ipc_dev->cmd);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ipc_dev_simple_cmd() - Send simple IPC command
+ * @ipc_dev : Reference to ipc device.
+ * @cmd : IPC command code.
+ *
+ * Send a simple IPC command to ipc device.
+ * Use this when don't need to specify input/output data
+ * and source/dest pointers.
+ *
+ * Return: an IPC error code or 0 on success.
+ */
+
+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)
+{
+ int ret;
+
+ ipc_dev_lock(ipc_dev);
+ ipc_dev_send_cmd(ipc_dev, cmd);
+ ret = ipc_dev_check_status(ipc_dev);
+ ipc_dev_unlock(ipc_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);
+
+/**
+ * ipc_dev_raw_cmd() - IPC command with data and pointers
+ * @ipc_dev : reference to ipc_dev.
+ * @cmd : IPC command code.
+ * @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. Use 0 if want to skip.
+ * @dptr : data writing to DPTR register. Use 0 if want to skip.
+ *
+ * Send an IPC command to device with input/output data and
+ * source/dest pointers.
+ *
+ * Return: an IPC error code or 0 on success.
+ */
+
+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd,
+ u8 *in, u32 inlen, u32 *out,
+ u32 outlen, u32 dptr, u32 sptr)
+{
+ u32 wbuf[4] = { 0 };
+ int ret;
+ int i;
+
+ ipc_dev_lock(ipc_dev);
+
+ memcpy(wbuf, in, inlen);
+
+ /* write if dptr_reg is valid */
+ if (ipc_dev->cfg->dptr_reg)
+ writel(dptr, ipc_dev->cfg->dptr_reg);
+
+ /* write if sptr_reg is valid */
+ if (ipc_dev->cfg->sptr_reg)
+ writel(sptr, ipc_dev->cfg->sptr_reg);
+
+ /* The input data register is 32bit register and inlen
+ * is in Byte */
+ for (i = 0; i < ((inlen + 3) / 4); i++)
+ ipc_dev_write_datal(ipc_dev, wbuf[i], 4 * i);
+
+ ipc_dev_send_cmd(ipc_dev, cmd);
+
+ ret = ipc_dev_check_status(ipc_dev);
+
+ /* The out data register is 32bit register and outlen
+ * is in 32 bit */
+ if (!ret) {
+ for (i = 0; i < outlen; i++)
+ *out++ = ipc_dev_read_datal(ipc_dev, 4 * i);
+ }
+
+ ipc_dev_unlock(ipc_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);
+
+/* sysfs option to send simple IPC commands from userspace */
+static ssize_t ipc_dev_cmd_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);
+ u32 cmd;
+ int ret;
+
+ ret = sscanf(buf, "%d", &cmd);
+ if (ret != 1) {
+ dev_err(dev, "Error args\n");
+ return -EINVAL;
+ }
+
+ ret = ipc_dev_simple_cmd(ipc_dev, cmd);
+ if (ret) {
+ dev_err(dev, "command 0x%x error with %d\n", cmd, ret);
+ return ret;
+ }
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);
+
+static struct attribute *ipc_dev_attrs[] = {
+ &dev_attr_send_cmd.attr,
+ NULL
+};
+
+static const struct attribute_group ipc_dev_group = {
+ .attrs = ipc_dev_attrs,
+};
+
+static const struct attribute_group *ipc_dev_groups[] = {
+ &ipc_dev_group,
+ NULL,
+};
+
+/* IPC device IRQ handler */
+static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id)
+{
+ struct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;
+
+ complete(&ipc_dev->cmd_complete);
+
+ return IRQ_HANDLED;
+}
+
+static void devm_intel_ipc_dev_release(struct device *dev, void *res)
+{
+ struct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;
+
+ if (!ipc_dev)
+ return;
+
+ device_del(&ipc_dev->dev);
+
+ kfree(ipc_dev);
+}
+
+/**
+ * devm_intel_ipc_dev_create() - Create IPC device
+ * @dev : IPC parent device.
+ * @devname : Name of the IPC device.
+ * @cfg : IPC device configuration.
+ * @ops : IPC device ops.
+ *
+ * Resource managed API to create IPC device with
+ * given configuration.
+ *
+ * Return : IPC device pointer or ERR_PTR(error code).
+ */
+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,
+ const char *devname,
+ struct intel_ipc_dev_cfg *cfg,
+ struct intel_ipc_dev_ops *ops)
+{
+ struct intel_ipc_dev **ptr, *ipc_dev;
+ int ret;
+
+ if (!dev && !devname && !cfg)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ ipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);
+ if (!ipc_dev) {
+ ret = -ENOMEM;
+ goto err_dev_create;
+ }
+
+ ipc_dev->dev.class = &intel_ipc_class;
+ ipc_dev->dev.parent = dev;
+ ipc_dev->dev.groups = ipc_dev_groups;
+ ipc_dev->cfg = cfg;
+ ipc_dev->ops = ops;
+
+ mutex_init(&ipc_dev->lock);
+ init_completion(&ipc_dev->cmd_complete);
+ dev_set_drvdata(&ipc_dev->dev, ipc_dev);
+ dev_set_name(&ipc_dev->dev, devname);
+ device_initialize(&ipc_dev->dev);
+
+ ret = device_add(&ipc_dev->dev);
+ if (ret < 0) {
+ dev_err(&ipc_dev->dev, "%s device create failed\n",
+ __func__);
+ ret = -ENODEV;
+ goto err_dev_add;
+ }
+
+ if (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {
+ if (devm_request_irq(&ipc_dev->dev,
+ ipc_dev->cfg->irq,
+ ipc_dev_irq_handler,
+ ipc_dev->cfg->irqflags,
+ dev_name(&ipc_dev->dev),
+ ipc_dev)) {
+ dev_err(&ipc_dev->dev,
+ "Failed to request irq\n");
+ goto err_irq_request;
+ }
+ }
+
+ *ptr = ipc_dev;
+
+ devres_add(dev, ptr);
+
+ return ipc_dev;
+
+err_irq_request:
+ device_del(&ipc_dev->dev);
+err_dev_add:
+ kfree(ipc_dev);
+err_dev_create:
+ devres_free(ptr);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);
+
+subsys_initcall(intel_ipc_init);
+module_exit(intel_ipc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kuppuswamy Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>");
+MODULE_DESCRIPTION("Intel IPC device class driver");
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 2/6] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe |
| Message-ID | <u9JVU-7qE-31@gated-at.bofh.it> |
| In reply to | #1701317 |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
This patch cleans up unnecessary free/alloc calls in this driver
by using devm_* calls.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
drivers/platform/x86/intel_pmc_ipc.c | 68 +++++++++++-------------------------
1 file changed, 21 insertions(+), 47 deletions(-)
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 7b65237..ad0416e 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -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;
}
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 6/6] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls |
| Message-ID | <u9JVU-7qE-37@gated-at.bofh.it> |
| In reply to | #1701317 |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
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.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
drivers/platform/x86/intel_pmc_ipc.c | 302 ++++++++++++-----------------------
1 file changed, 103 insertions(+), 199 deletions(-)
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 43533ec..675f8d9 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -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;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | sathyanarayanan.kuppuswamy@linux.intel.com |
|---|---|
| Date | 2017-08-01 20:20 +0200 |
| Subject | [RFC v1 5/6] platform/x86: intel_punit_ipc: Use generic intel ipc device calls |
| Message-ID | <u9JVU-7qE-39@gated-at.bofh.it> |
| In reply to | #1701317 |
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
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.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
drivers/platform/x86/intel_punit_ipc.c | 234 ++++++++++++---------------------
1 file changed, 84 insertions(+), 150 deletions(-)
diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
index a47a41f..d113987 100644
--- a/drivers/platform/x86/intel_punit_ipc.c
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -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),
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Andy Shevchenko <andy.shevchenko@gmail.com> |
|---|---|
| Date | 2017-08-06 20:40 +0200 |
| Message-ID | <ubyD0-5N3-19@gated-at.bofh.it> |
| In reply to | #1701317 |
On Tue, Aug 1, 2017 at 9:13 PM,
<sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>
> Hi All,
>
> Currently intel_pmc_ipc.c, intel_punit_ipc.c, intel_scu_ipc.c drivers implements the same IPC features.
> This code duplication could be avoided if we implement the IPC driver as a 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.
>
Thanks for doing it, really appreciated!
I'm on vacation for few days now, I will definitely check this after.
> 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
>
--
With Best Regards,
Andy Shevchenko
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web