get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/809930/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 809930,
    "url": "http://patchwork.ozlabs.org/api/patches/809930/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/rtc-linux/patch/7d1a79925df8d2e35cfbd6974706d0d1ef0fc0de.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com/",
    "project": {
        "id": 9,
        "url": "http://patchwork.ozlabs.org/api/projects/9/?format=api",
        "name": "Linux RTC development",
        "link_name": "rtc-linux",
        "list_id": "linux-rtc.vger.kernel.org",
        "list_email": "linux-rtc@vger.kernel.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<7d1a79925df8d2e35cfbd6974706d0d1ef0fc0de.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>",
    "list_archive_url": null,
    "date": "2017-09-05T05:37:24",
    "name": "[RFC,v3,4/7] platform: x86: Add generic Intel IPC driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": false,
    "hash": "4008aaba2b0408cd89d6a84f21812bd7fcaa0461",
    "submitter": {
        "id": 66129,
        "url": "http://patchwork.ozlabs.org/api/people/66129/?format=api",
        "name": "Kuppuswamy Sathyanarayanan",
        "email": "sathyanarayanan.kuppuswamy@linux.intel.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/rtc-linux/patch/7d1a79925df8d2e35cfbd6974706d0d1ef0fc0de.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com/mbox/",
    "series": [
        {
            "id": 1490,
            "url": "http://patchwork.ozlabs.org/api/series/1490/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/rtc-linux/list/?series=1490",
            "date": "2017-09-05T05:37:20",
            "name": "PMC/PUNIT IPC driver cleanup",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/1490/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/809930/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/809930/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<linux-rtc-owner@vger.kernel.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-rtc-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)",
        "Received": [
            "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xmbBY6XB7z9sNq\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue,  5 Sep 2017 15:39:33 +1000 (AEST)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1754182AbdIEFjG (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tTue, 5 Sep 2017 01:39:06 -0400",
            "from mga01.intel.com ([192.55.52.88]:3891 \"EHLO mga01.intel.com\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1753819AbdIEFiP (ORCPT <rfc822;linux-rtc@vger.kernel.org>);\n\tTue, 5 Sep 2017 01:38:15 -0400",
            "from orsmga005.jf.intel.com ([10.7.209.41])\n\tby fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t04 Sep 2017 22:38:13 -0700",
            "from skuppusw-desk.jf.intel.com ([10.7.198.92])\n\tby orsmga005.jf.intel.com with ESMTP; 04 Sep 2017 22:38:13 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.41,478,1498546800\"; d=\"scan'208\";a=\"145505102\"",
        "From": "sathyanarayanan.kuppuswamy@linux.intel.com",
        "To": "a.zummo@towertech.it, x86@kernel.org, wim@iguana.be,\n\tmingo@redhat.com, alexandre.belloni@free-electrons.com,\n\tqipeng.zha@intel.com, hpa@zytor.com, dvhart@infradead.org,\n\ttglx@linutronix.de, lee.jones@linaro.org, andy@infradead.org,\n\tsouvik.k.chakravarty@intel.com",
        "Cc": "linux-rtc@vger.kernel.org, linux-watchdog@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org,\n\tsathyaosid@gmail.com, Kuppuswamy Sathyanarayanan \n\t<sathyanarayanan.kuppuswamy@linux.intel.com>",
        "Subject": "[RFC v3 4/7] platform: x86: Add generic Intel IPC driver",
        "Date": "Mon,  4 Sep 2017 22:37:24 -0700",
        "Message-Id": "<7d1a79925df8d2e35cfbd6974706d0d1ef0fc0de.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<cover.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>",
        "References": "<cover.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>",
        "Sender": "linux-rtc-owner@vger.kernel.org",
        "Precedence": "bulk",
        "List-ID": "<linux-rtc.vger.kernel.org>",
        "X-Mailing-List": "linux-rtc@vger.kernel.org"
    },
    "content": "From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>\n\nCurrently intel_scu_ipc.c, intel_pmc_ipc.c and intel_punit_ipc.c\nredundantly implements the same IPC features and has lot of code\nduplication between them. This driver addresses this issue by grouping\nthe common IPC functionalities under the same driver.\n\nSigned-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>\n---\n drivers/platform/x86/Kconfig                    |   8 +\n drivers/platform/x86/Makefile                   |   1 +\n drivers/platform/x86/intel_ipc_dev.c            | 539 ++++++++++++++++++++++++\n include/linux/platform_data/x86/intel_ipc_dev.h | 187 ++++++++\n 4 files changed, 735 insertions(+)\n create mode 100644 drivers/platform/x86/intel_ipc_dev.c\n create mode 100644 include/linux/platform_data/x86/intel_ipc_dev.h\n\nChanges since v2:\n * Added ipc_dev_cmd API support.",
    "diff": "diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig\nindex 45f4e79..9df7cda 100644\n--- a/drivers/platform/x86/Kconfig\n+++ b/drivers/platform/x86/Kconfig\n@@ -1140,6 +1140,14 @@ config SILEAD_DMI\n \t  with the OS-image for the device. This option supplies the missing\n \t  information. Enable this for x86 tablets with Silead touchscreens.\n \n+config INTEL_IPC_DEV\n+\tbool \"Intel IPC Device Driver\"\n+\tdepends on X86_64\n+\t---help---\n+\t  This driver implements core features of Intel IPC device. Devices\n+\t  like PMC, SCU, PUNIT, etc can use interfaces provided by this\n+\t  driver to implement IPC protocol of their respective device.\n+\n endif # X86_PLATFORM_DEVICES\n \n config PMC_ATOM\ndiff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile\nindex 91cec17..04e11ce 100644\n--- a/drivers/platform/x86/Makefile\n+++ b/drivers/platform/x86/Makefile\n@@ -83,3 +83,4 @@ obj-$(CONFIG_PMC_ATOM)\t\t+= pmc_atom.o\n obj-$(CONFIG_MLX_PLATFORM)\t+= mlx-platform.o\n obj-$(CONFIG_MLX_CPLD_PLATFORM)\t+= mlxcpld-hotplug.o\n obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o\n+obj-$(CONFIG_INTEL_IPC_DEV)\t+= intel_ipc_dev.o\ndiff --git a/drivers/platform/x86/intel_ipc_dev.c b/drivers/platform/x86/intel_ipc_dev.c\nnew file mode 100644\nindex 0000000..65db8bb\n--- /dev/null\n+++ b/drivers/platform/x86/intel_ipc_dev.c\n@@ -0,0 +1,539 @@\n+/*\n+ * intel_ipc_dev.c: Intel IPC device class driver\n+ *\n+ * (C) Copyright 2017 Intel Corporation\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; version 2\n+ * of the License.\n+ *\n+ */\n+\n+#include <linux/device.h>\n+#include <linux/module.h>\n+#include <linux/delay.h>\n+#include <linux/err.h>\n+#include <linux/export.h>\n+#include <linux/idr.h>\n+#include <linux/init.h>\n+#include <linux/slab.h>\n+#include <linux/interrupt.h>\n+#include <linux/platform_data/x86/intel_ipc_dev.h>\n+#include <linux/regmap.h>\n+\n+/* mutex to sync different ipc devices in same channel */\n+static struct mutex channel_lock[IPC_CHANNEL_MAX];\n+\n+static void ipc_channel_lock_init(void)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < IPC_CHANNEL_MAX; i++)\n+\t\tmutex_init(&channel_lock[i]);\n+}\n+\n+static struct class intel_ipc_class = {\n+\t.name = \"intel_ipc\",\n+\t.owner = THIS_MODULE,\n+};\n+\n+static int ipc_dev_lock(struct intel_ipc_dev *ipc_dev)\n+{\n+\tint chan_type;\n+\n+\tif (!ipc_dev || !ipc_dev->cfg)\n+\t\treturn -ENODEV;\n+\n+\tchan_type = ipc_dev->cfg->chan_type;\n+\tif (chan_type > IPC_CHANNEL_MAX)\n+\t\treturn -EINVAL;\n+\n+\t/* acquire channel lock */\n+\tmutex_lock(&channel_lock[chan_type]);\n+\n+\t/* acquire IPC device lock */\n+\tmutex_lock(&ipc_dev->lock);\n+\n+\treturn 0;\n+}\n+\n+static int ipc_dev_unlock(struct intel_ipc_dev *ipc_dev)\n+{\n+\tint chan_type;\n+\n+\tif (!ipc_dev || !ipc_dev->cfg)\n+\t\treturn -ENODEV;\n+\n+\tchan_type = ipc_dev->cfg->chan_type;\n+\tif (chan_type > IPC_CHANNEL_MAX)\n+\t\treturn -EINVAL;\n+\n+\t/* release IPC device lock */\n+\tmutex_unlock(&ipc_dev->lock);\n+\n+\t/* release channel lock */\n+\tmutex_unlock(&channel_lock[chan_type]);\n+\n+\treturn 0;\n+}\n+\n+static const char *ipc_dev_err_string(struct intel_ipc_dev *ipc_dev,\n+\tint error)\n+{\n+\tswitch (error) {\n+\tcase IPC_DEV_ERR_NONE:\n+\t\treturn \"No error\";\n+\tcase IPC_DEV_ERR_CMD_NOT_SUPPORTED:\n+\t\treturn \"Command not-supported/Invalid\";\n+\tcase IPC_DEV_ERR_CMD_NOT_SERVICED:\n+\t\treturn \"Command not-serviced/Invalid param\";\n+\tcase IPC_DEV_ERR_UNABLE_TO_SERVICE:\n+\t\treturn \"Unable-to-service/Cmd-timeout\";\n+\tcase IPC_DEV_ERR_CMD_INVALID:\n+\t\treturn \"Command-invalid/Cmd-locked\";\n+\tcase IPC_DEV_ERR_CMD_FAILED:\n+\t\treturn \"Command-failed/Invalid-VR-id\";\n+\tcase IPC_DEV_ERR_EMSECURITY:\n+\t\treturn \"Invalid Battery/VR-Error\";\n+\tcase IPC_DEV_ERR_UNSIGNEDKERNEL:\n+\t\treturn \"Unsigned kernel\";\n+\tdefault:\n+\t\treturn \"Unknown Command\";\n+\t};\n+}\n+\n+/* Helper function to send given command to IPC device */\n+static inline void ipc_dev_send_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd)\n+{\n+\tipc_dev->cmd = cmd;\n+\n+\tif (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ)\n+\t\treinit_completion(&ipc_dev->cmd_complete);\n+\n+\tif (ipc_dev->ops->enable_msi)\n+\t\tcmd = ipc_dev->ops->enable_msi(cmd);\n+\n+\tregmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->cmd_reg, cmd);\n+}\n+\n+static inline int ipc_dev_status_busy(struct intel_ipc_dev *ipc_dev)\n+{\n+\tint status;\n+\n+\tregmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);\n+\n+\tif (ipc_dev->ops->busy_check)\n+\t\treturn ipc_dev->ops->busy_check(status);\n+\n+\treturn 0;\n+}\n+\n+/* Check the status of IPC command and return err code if failed */\n+static int ipc_dev_check_status(struct intel_ipc_dev *ipc_dev)\n+{\n+\tint loop_count = IPC_DEV_CMD_LOOP_CNT;\n+\tint status;\n+\tint ret = 0;\n+\n+\tif (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {\n+\t\tif (!wait_for_completion_timeout(&ipc_dev->cmd_complete,\n+\t\t\t\tIPC_DEV_CMD_TIMEOUT))\n+\t\t\tret = -ETIMEDOUT;\n+\t} else {\n+\t\twhile (ipc_dev_status_busy(ipc_dev) && --loop_count)\n+\t\t\tudelay(1);\n+\t\tif (!loop_count)\n+\t\t\tret = -ETIMEDOUT;\n+\t}\n+\n+\tif (ret < 0) {\n+\t\tdev_err(&ipc_dev->dev,\n+\t\t\t\t\"IPC timed out, CMD=0x%x\\n\", ipc_dev->cmd);\n+\t\treturn ret;\n+\t}\n+\n+\tregmap_read(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->status_reg, &status);\n+\n+\tif (ipc_dev->ops->to_err_code)\n+\t\tret = ipc_dev->ops->to_err_code(status);\n+\n+\tif (ret) {\n+\t\tdev_err(&ipc_dev->dev,\n+\t\t\t\t\"IPC failed: %s, STS=0x%x, CMD=0x%x\\n\",\n+\t\t\t\tipc_dev_err_string(ipc_dev, ret),\n+\t\t\t\tstatus, ipc_dev->cmd);\n+\t\treturn -EIO;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * ipc_dev_simple_cmd() - Send simple IPC command\n+ * @ipc_dev     : Reference to ipc device.\n+ * @cmd_list    : IPC command list.\n+ * @cmdlen      : Number of cmd/sub-cmds.\n+ *\n+ * Send a simple IPC command to ipc device.\n+ * Use this when don't need to specify input/output data\n+ * and source/dest pointers.\n+ *\n+ * Return:\tan IPC error code or 0 on success.\n+ */\n+\n+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,\n+\t\tu32 cmdlen)\n+{\n+\tint ret;\n+\n+\tif (!cmd_list)\n+\t\treturn -EINVAL;\n+\n+\tret = ipc_dev_lock(ipc_dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Call custom pre-processing handler */\n+\tif (ipc_dev->ops->pre_simple_cmd_fn) {\n+\t\tret = ipc_dev->ops->pre_simple_cmd_fn(cmd_list, cmdlen);\n+\t\tif (ret)\n+\t\t\tgoto unlock_device;\n+\t}\n+\n+\tipc_dev_send_cmd(ipc_dev, cmd_list[0]);\n+\n+\tret = ipc_dev_check_status(ipc_dev);\n+\n+unlock_device:\n+\tipc_dev_unlock(ipc_dev);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(ipc_dev_simple_cmd);\n+\n+/**\n+ * ipc_dev_cmd() - Send IPC command with data.\n+ * @ipc_dev     : Reference to ipc_dev.\n+ * @cmd_list    : Array of commands/sub-commands.\n+ * @cmdlen      : Number of commands.\n+ * @in          : Input data of this IPC command.\n+ * @inlen       : Input data length in dwords.\n+ * @out         : Output data of this IPC command.\n+ * @outlen      : Length of output data in dwords.\n+ *\n+ * Send an IPC command to device with input/output data.\n+ *\n+ * Return:\tan IPC error code or 0 on success.\n+ */\n+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,\n+\t\tu32 *in, u32 inlen, u32 *out, u32 outlen)\n+{\n+\tint ret;\n+\n+\tif (!cmd_list || !in)\n+\t\treturn -EINVAL;\n+\n+\tret = ipc_dev_lock(ipc_dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Call custom pre-processing handler. */\n+\tif (ipc_dev->ops->pre_cmd_fn) {\n+\t\tret = ipc_dev->ops->pre_cmd_fn(cmd_list, cmdlen, in, inlen,\n+\t\t\t\tout, outlen);\n+\t\tif (ret)\n+\t\t\tgoto unlock_device;\n+\t}\n+\n+\t/* Write inlen dwords of data to wrbuf_reg. */\n+\tif (inlen > 0)\n+\t\tregmap_bulk_write(ipc_dev->cfg->data_regs,\n+\t\t\t\tipc_dev->cfg->wrbuf_reg, in, inlen);\n+\n+\tipc_dev_send_cmd(ipc_dev, cmd_list[0]);\n+\n+\tret = ipc_dev_check_status(ipc_dev);\n+\n+\t/* Read outlen dwords of data from rbug_reg. */\n+\tif (!ret && outlen > 0)\n+\t\tregmap_bulk_read(ipc_dev->cfg->data_regs,\n+\t\t\t\tipc_dev->cfg->rbuf_reg, out, outlen);\n+unlock_device:\n+\tipc_dev_unlock(ipc_dev);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(ipc_dev_cmd);\n+\n+/**\n+ * ipc_dev_raw_cmd() - Send IPC command with data and pointers.\n+ * @ipc_dev     : Reference to ipc_dev.\n+ * @cmd_list    : Array of commands/sub-commands.\n+ * @cmdlen      : Number of commands.\n+ * @in          : Input data of this IPC command.\n+ * @inlen       : Input data length in bytes.\n+ * @out         : Output data of this IPC command.\n+ * @outlen      : Length of output data in dwords.\n+ * @dptr        : IPC destination data address.\n+ * @sptr        : IPC source data address.\n+ *\n+ * Send an IPC command to device with input/output data and\n+ * source/dest pointers.\n+ *\n+ * Return:\tan IPC error code or 0 on success.\n+ */\n+\n+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,\n+\t\tu8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr)\n+{\n+\tint ret;\n+\tint inbuflen = DIV_ROUND_UP(inlen, 4);\n+\tu32 *inbuf;\n+\n+\tif (!cmd_list || !in)\n+\t\treturn -EINVAL;\n+\n+\tinbuf = kzalloc(inbuflen, GFP_KERNEL);\n+\tif (!inbuf)\n+\t\treturn -ENOMEM;\n+\n+\tret = ipc_dev_lock(ipc_dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Call custom pre-processing handler. */\n+\tif (ipc_dev->ops->pre_raw_cmd_fn) {\n+\t\tret = ipc_dev->ops->pre_raw_cmd_fn(cmd_list, cmdlen, in, inlen,\n+\t\t\t\tout, outlen, dptr, sptr);\n+\t\tif (ret)\n+\t\t\tgoto unlock_device;\n+\t}\n+\n+\t/* If supported, write DPTR register.*/\n+\tif (ipc_dev->cfg->support_dptr)\n+\t\tregmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->dptr_reg,\n+\t\t\t\tdptr);\n+\n+\t/* If supported, write SPTR register. */\n+\tif (ipc_dev->cfg->support_sptr)\n+\t\tregmap_write(ipc_dev->cfg->cmd_regs, ipc_dev->cfg->sptr_reg,\n+\t\t\t\tsptr);\n+\n+\tmemcpy(inbuf, in, inlen);\n+\n+\t/* Write inlen dwords of data to wrbuf_reg. */\n+\tif (inlen > 0)\n+\t\tregmap_bulk_write(ipc_dev->cfg->data_regs,\n+\t\t\t\tipc_dev->cfg->wrbuf_reg, inbuf, inbuflen);\n+\n+\tipc_dev_send_cmd(ipc_dev, cmd_list[0]);\n+\n+\tret = ipc_dev_check_status(ipc_dev);\n+\n+\t/* Read outlen dwords of data from rbug_reg. */\n+\tif (!ret && outlen > 0)\n+\t\tregmap_bulk_read(ipc_dev->cfg->data_regs,\n+\t\t\t\tipc_dev->cfg->rbuf_reg, out, outlen);\n+unlock_device:\n+\tipc_dev_unlock(ipc_dev);\n+\tkfree(inbuf);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(ipc_dev_raw_cmd);\n+\n+/* sysfs option to send simple IPC commands from userspace */\n+static ssize_t ipc_dev_cmd_reg_store(struct device *dev,\n+\t\t\t\t     struct device_attribute *attr,\n+\t\t\t\t     const char *buf, size_t count)\n+{\n+\tstruct intel_ipc_dev *ipc_dev = dev_get_drvdata(dev);\n+\tu32 cmd;\n+\tint ret;\n+\n+\tret = sscanf(buf, \"%d\", &cmd);\n+\tif (ret != 1) {\n+\t\tdev_err(dev, \"Error args\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = ipc_dev_simple_cmd(ipc_dev, &cmd, 1);\n+\tif (ret) {\n+\t\tdev_err(dev, \"command 0x%x error with %d\\n\", cmd, ret);\n+\t\treturn ret;\n+\t}\n+\treturn (ssize_t)count;\n+}\n+\n+static DEVICE_ATTR(send_cmd, S_IWUSR, NULL, ipc_dev_cmd_reg_store);\n+\n+static struct attribute *ipc_dev_attrs[] = {\n+\t&dev_attr_send_cmd.attr,\n+\tNULL\n+};\n+\n+static const struct attribute_group ipc_dev_group = {\n+\t.attrs = ipc_dev_attrs,\n+};\n+\n+static const struct attribute_group *ipc_dev_groups[] = {\n+\t&ipc_dev_group,\n+\tNULL,\n+};\n+\n+/* IPC device IRQ handler */\n+static irqreturn_t ipc_dev_irq_handler(int irq, void *dev_id)\n+{\n+\tstruct intel_ipc_dev *ipc_dev = (struct intel_ipc_dev *)dev_id;\n+\n+\tcomplete(&ipc_dev->cmd_complete);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static void devm_intel_ipc_dev_release(struct device *dev, void *res)\n+{\n+\tstruct intel_ipc_dev *ipc_dev = *(struct intel_ipc_dev **)res;\n+\n+\tif (!ipc_dev)\n+\t\treturn;\n+\n+\tdevice_del(&ipc_dev->dev);\n+\n+\tkfree(ipc_dev);\n+}\n+\n+static int match_name(struct device *dev, const void *data)\n+{\n+        if (!dev_name(dev))\n+                return 0;\n+\n+        return !strcmp(dev_name(dev), (char *)data);\n+}\n+\n+/**\n+ * intel_ipc_dev_get() - Get Intel IPC device from name.\n+ * @dev_name    : Name of the IPC device.\n+ *\n+ * Return       : ERR_PTR/NULL or intel_ipc_dev pointer on success.\n+ */\n+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)\n+{\n+        struct device *dev;\n+\n+\tif (!dev_name)\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tdev = class_find_device(&intel_ipc_class, NULL, dev_name, match_name);\n+\tif (dev)\n+\t\tput_device(dev);\n+\n+\treturn dev_get_drvdata(dev);\n+}\n+EXPORT_SYMBOL_GPL(intel_ipc_dev_get);\n+\n+/**\n+ * devm_intel_ipc_dev_create() - Create IPC device\n+ * @dev\t\t: IPC parent device.\n+ * @devname\t: Name of the IPC device.\n+ * @cfg\t\t: IPC device configuration.\n+ * @ops\t\t: IPC device ops.\n+ *\n+ * Resource managed API to create IPC device with\n+ * given configuration.\n+ *\n+ * Return\t: IPC device pointer or ERR_PTR(error code).\n+ */\n+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,\n+\t\tconst char *devname,\n+\t\tstruct intel_ipc_dev_cfg *cfg,\n+\t\tstruct intel_ipc_dev_ops *ops)\n+{\n+\tstruct intel_ipc_dev **ptr, *ipc_dev;\n+\tint ret;\n+\n+\tif (!dev && !devname && !cfg)\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (!intel_ipc_dev_get(devname)) {\n+\t\tdev_err(dev, \"IPC device %s already exist\\n\", devname);\n+\t\treturn ERR_PTR(-EINVAL);\n+\t}\n+\n+\tptr = devres_alloc(devm_intel_ipc_dev_release, sizeof(*ptr),\n+\t\t\tGFP_KERNEL);\n+\tif (!ptr)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tipc_dev = kzalloc(sizeof(*ipc_dev), GFP_KERNEL);\n+\tif (!ipc_dev) {\n+\t\tret = -ENOMEM;\n+\t\tgoto err_dev_create;\n+\t}\n+\n+\tipc_dev->dev.class = &intel_ipc_class;\n+\tipc_dev->dev.parent = dev;\n+\tipc_dev->dev.groups = ipc_dev_groups;\n+\tipc_dev->cfg = cfg;\n+\tipc_dev->ops = ops;\n+\n+\tmutex_init(&ipc_dev->lock);\n+\tinit_completion(&ipc_dev->cmd_complete);\n+\tdev_set_drvdata(&ipc_dev->dev, ipc_dev);\n+\tdev_set_name(&ipc_dev->dev, devname);\n+\tdevice_initialize(&ipc_dev->dev);\n+\n+\tret = device_add(&ipc_dev->dev);\n+\tif (ret < 0) {\n+\t\tdev_err(&ipc_dev->dev, \"%s device create failed\\n\",\n+\t\t\t\t__func__);\n+\t\tret = -ENODEV;\n+\t\tgoto err_dev_add;\n+\t}\n+\n+\tif (ipc_dev->cfg->mode == IPC_DEV_MODE_IRQ) {\n+\t\tif (devm_request_irq(&ipc_dev->dev,\n+\t\t\t\tipc_dev->cfg->irq,\n+\t\t\t\tipc_dev_irq_handler,\n+\t\t\t\tipc_dev->cfg->irqflags,\n+\t\t\t\tdev_name(&ipc_dev->dev),\n+\t\t\t\tipc_dev)) {\n+\t\t\tdev_err(&ipc_dev->dev,\n+\t\t\t\t\t\"Failed to request irq\\n\");\n+\t\t\tgoto err_irq_request;\n+\t\t}\n+\t}\n+\n+\t*ptr = ipc_dev;\n+\n+\tdevres_add(dev, ptr);\n+\n+\treturn ipc_dev;\n+\n+err_irq_request:\n+\tdevice_del(&ipc_dev->dev);\n+err_dev_add:\n+\tkfree(ipc_dev);\n+err_dev_create:\n+\tdevres_free(ptr);\n+\treturn ERR_PTR(ret);\n+}\n+EXPORT_SYMBOL_GPL(devm_intel_ipc_dev_create);\n+\n+static int __init intel_ipc_init(void)\n+{\n+\tipc_channel_lock_init();\n+\treturn class_register(&intel_ipc_class);\n+}\n+\n+static void __exit intel_ipc_exit(void)\n+{\n+\tclass_unregister(&intel_ipc_class);\n+}\n+subsys_initcall(intel_ipc_init);\n+module_exit(intel_ipc_exit);\n+\n+MODULE_LICENSE(\"GPL v2\");\n+MODULE_AUTHOR(\"Kuppuswamy Sathyanarayanan<sathyanarayanan.kuppuswamy@linux.intel.com>\");\n+MODULE_DESCRIPTION(\"Intel IPC device class driver\");\ndiff --git a/include/linux/platform_data/x86/intel_ipc_dev.h b/include/linux/platform_data/x86/intel_ipc_dev.h\nnew file mode 100644\nindex 0000000..9485740\n--- /dev/null\n+++ b/include/linux/platform_data/x86/intel_ipc_dev.h\n@@ -0,0 +1,187 @@\n+/*\n+ * Intel IPC class device header file.\n+ *\n+ * (C) Copyright 2017 Intel Corporation\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; version 2\n+ * of the License.\n+ *\n+ */\n+\n+#ifndef INTEL_IPC_DEV_H\n+#define INTEL_IPC_DEV_H\n+\n+#include <linux/module.h>\n+#include <linux/device.h>\n+\n+/* IPC channel type */\n+#define IPC_CHANNEL_IA_PMC                      0\n+#define IPC_CHANNEL_IA_PUNIT                    1\n+#define IPC_CHANNEL_PMC_PUNIT                   2\n+#define IPC_CHANNEL_IA_SCU                      3\n+#define IPC_CHANNEL_MAX                         4\n+\n+/* IPC return code */\n+#define IPC_DEV_ERR_NONE\t\t\t0\n+#define IPC_DEV_ERR_CMD_NOT_SUPPORTED\t\t1\n+#define IPC_DEV_ERR_CMD_NOT_SERVICED\t\t2\n+#define IPC_DEV_ERR_UNABLE_TO_SERVICE\t\t3\n+#define IPC_DEV_ERR_CMD_INVALID\t\t\t4\n+#define IPC_DEV_ERR_CMD_FAILED\t\t\t5\n+#define IPC_DEV_ERR_EMSECURITY\t\t\t6\n+#define IPC_DEV_ERR_UNSIGNEDKERNEL\t\t7\n+\n+/* IPC mode */\n+#define IPC_DEV_MODE_IRQ\t\t\t0\n+#define IPC_DEV_MODE_POLLING\t\t\t1\n+\n+/* IPC dev constants */\n+#define IPC_DEV_CMD_LOOP_CNT\t\t\t3000000\n+#define IPC_DEV_CMD_TIMEOUT\t\t\t3 * HZ\n+#define IPC_DEV_DATA_BUFFER_SIZE\t\t16\n+\n+struct intel_ipc_dev;\n+struct intel_ipc_raw_cmd;\n+\n+/**\n+ * struct intel_ipc_dev_cfg - IPC device config structure.\n+ *\n+ * IPC device drivers uses the following config options to\n+ * register new IPC device.\n+ *\n+ * @cmd_regs            : IPC device command base regmap.\n+ * @data_regs           : IPC device data base regmap.\n+ * @wrbuf_reg           : IPC device data write register address.\n+ * @rbuf_reg            : IPC device data read register address.\n+ * @sptr_reg            : IPC device source data pointer register address.\n+ * @dptr_reg            : IPC device destination data pointer register\n+ *                        address.\n+ * @status_reg          : IPC command status register address.\n+ * @cmd_reg             : IPC command register address.\n+ * @mode                : IRQ/POLLING mode.\n+ * @irq                 : IPC device IRQ number.\n+ * @irqflags            : IPC device IRQ flags.\n+ * @chan_type           : IPC device channel type(PMC/PUNIT).\n+ * @msi                 : Enable/Disable MSI for IPC commands.\n+ * @support_dptr        : Support DPTR update.\n+ * @support_sptr        : Support SPTR update.\n+ *\n+ */\n+struct intel_ipc_dev_cfg {\n+\tstruct regmap *cmd_regs;\n+\tstruct regmap *data_regs;\n+\tunsigned int wrbuf_reg;\n+\tunsigned int rbuf_reg;\n+\tunsigned int sptr_reg;\n+\tunsigned int dptr_reg;\n+\tunsigned int status_reg;\n+\tunsigned int cmd_reg;\n+\tint mode;\n+\tint irq;\n+\tint irqflags;\n+\tint chan_type;\n+\tbool use_msi;\n+\tbool support_dptr;\n+\tbool support_sptr;\n+};\n+\n+/**\n+ * struct intel_ipc_dev_ops - IPC device ops structure.\n+ *\n+ * Call backs for IPC device specific operations.\n+ *\n+ * @to_err_code         : Status to error code conversion function.\n+ * @busy_check          : Check for IPC busy status.\n+ * @enable_msi          : Enable MSI for IPC commands.\n+ * @pre_simple_cmd_fn   : Custom pre-processing function for\n+ *                        ipc_dev_simple_cmd()\n+ * @pre_cmd_fn          : Custom pre-processing function for\n+ *                        ipc_dev_cmd()\n+ * @pre_raw_cmd_fn      : Custom pre-processing function for\n+ *                        ipc_dev_raw_cmd()\n+ *\n+ */\n+struct intel_ipc_dev_ops {\n+\tint (*to_err_code)(int status);\n+\tint (*busy_check)(int status);\n+\tu32 (*enable_msi)(u32 cmd);\n+\tint (*pre_simple_cmd_fn)(u32 *cmd_list, u32 cmdlen);\n+\tint (*pre_cmd_fn)(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,\n+\t\t\tu32 *out, u32 outlen);\n+\tint (*pre_raw_cmd_fn)(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,\n+\t\t\tu32 *out, u32 outlen, u32 dptr, u32 sptr);\n+};\n+\n+/**\n+ * struct intel_ipc_dev - Intel IPC device structure.\n+ *\n+ * Used with devm_intel_ipc_dev_create() to create new IPC device.\n+ *\n+ * @dev                 : IPC device object.\n+ * @cmd                 : Current IPC device command.\n+ * @cmd_complete        : Command completion object.\n+ * @lock                : Lock to protect IPC device structure.\n+ * @ops                 : IPC device ops pointer.\n+ * @cfg                 : IPC device cfg pointer.\n+ *\n+ */\n+struct intel_ipc_dev {\n+\tstruct device dev;\n+\tint cmd;\n+\tstruct completion cmd_complete;\n+\tstruct mutex lock;\n+\tstruct intel_ipc_dev_ops *ops;\n+\tstruct intel_ipc_dev_cfg *cfg;\n+};\n+\n+#if IS_ENABLED(CONFIG_INTEL_IPC_DEV)\n+\n+/* API to create new IPC device */\n+struct intel_ipc_dev *devm_intel_ipc_dev_create(struct device *dev,\n+\t\tconst char *devname, struct intel_ipc_dev_cfg *cfg,\n+\t\tstruct intel_ipc_dev_ops *ops);\n+\n+int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,\n+\t\tu32 cmdlen);\n+int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,\n+\t\tu32 *in, u32 inlen, u32 *out, u32 outlen);\n+int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list, u32 cmdlen,\n+\t\tu8 *in, u32 inlen, u32 *out, u32 outlen, u32 dptr, u32 sptr);\n+struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name);\n+#else\n+\n+static inline struct intel_ipc_dev *devm_intel_ipc_dev_create(\n+\t\tstruct device *dev,\n+\t\tconst char *devname, struct intel_ipc_dev_cfg *cfg,\n+\t\tstruct intel_ipc_dev_ops *ops)\n+{\n+\treturn -EINVAL;\n+}\n+\n+static inline int ipc_dev_simple_cmd(struct intel_ipc_dev *ipc_dev,\n+\t\tu32 *cmd_list, u32 cmdlen)\n+{\n+\treturn -EINVAL;\n+}\n+\n+static int ipc_dev_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,\n+\t\tu32 cmdlen, u32 *in, u32 inlen, u32 *out, u32 outlen)\n+{\n+\treturn -EINVAL;\n+}\n+\n+static inline int ipc_dev_raw_cmd(struct intel_ipc_dev *ipc_dev, u32 *cmd_list,\n+\t\tu32 cmdlen, u8 *in, u32 inlen, u32 *out, u32 outlen,\n+\t\tu32 dptr, u32 sptr);\n+{\n+\treturn -EINVAL;\n+}\n+\n+static inline struct intel_ipc_dev *intel_ipc_dev_get(const char *dev_name)\n+{\n+\treturn NULL;\n+}\n+#endif /* CONFIG_INTEL_IPC_DEV */\n+#endif /* INTEL_IPC_DEV_H */\n",
    "prefixes": [
        "RFC",
        "v3",
        "4/7"
    ]
}