diff mbox

platform:x86: add Intel Punit mailbox IPC driver

Message ID 1438358336-79179-1-git-send-email-qipeng.zha@intel.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

qipeng.zha July 31, 2015, 3:58 p.m. UTC
This driver provides support for Punit mailbox IPC on Intel platforms.
The heart of the Punit is the Foxton microcontroller and its firmware,
which provide mailbox interface for power management usage.

Signed-off-by: Qipeng Zha <qipeng.zha@intel.com>
---
 arch/x86/include/asm/intel_punit_ipc.h | 101 +++++++++
 drivers/platform/x86/Kconfig           |   6 +
 drivers/platform/x86/Makefile          |   1 +
 drivers/platform/x86/intel_punit_ipc.c | 388 +++++++++++++++++++++++++++++++++
 4 files changed, 496 insertions(+)
 create mode 100644 arch/x86/include/asm/intel_punit_ipc.h
 create mode 100644 drivers/platform/x86/intel_punit_ipc.c

Comments

Darren Hart Aug. 5, 2015, 9:25 p.m. UTC | #1
On Fri, Jul 31, 2015 at 11:58:56PM +0800, Qipeng Zha wrote:
> This driver provides support for Punit mailbox IPC on Intel platforms.
> The heart of the Punit is the Foxton microcontroller and its firmware,
> which provide mailbox interface for power management usage.
> 

Mika, Andriy:

I'm traveling over the next few days and could use some help ensuring this new
driver receives some additional review. If you can, I'd appreciate your
thoughts.

> Signed-off-by: Qipeng Zha <qipeng.zha@intel.com>
> ---
>  arch/x86/include/asm/intel_punit_ipc.h | 101 +++++++++
>  drivers/platform/x86/Kconfig           |   6 +
>  drivers/platform/x86/Makefile          |   1 +
>  drivers/platform/x86/intel_punit_ipc.c | 388 +++++++++++++++++++++++++++++++++
>  4 files changed, 496 insertions(+)
>  create mode 100644 arch/x86/include/asm/intel_punit_ipc.h
>  create mode 100644 drivers/platform/x86/intel_punit_ipc.c
> 
> diff --git a/arch/x86/include/asm/intel_punit_ipc.h b/arch/x86/include/asm/intel_punit_ipc.h
> new file mode 100644
> index 0000000..08e3e14
> --- /dev/null
> +++ b/arch/x86/include/asm/intel_punit_ipc.h
> @@ -0,0 +1,101 @@
> +#ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
> +#define  _ASM_X86_INTEL_PUNIT_IPC_H_
> +
> +/* Commands supported on GLM core, are handled by different bars,
> + * unify these commands together here
> + */
> +#define IPC_BIOS_PUNIT_CMD_BASE				0x00
> +#define IPC_GTD_PUNIT_CMD_BASE				0x20
> +#define IPC_ISPD_PUNIT_CMD_BASE				0x40
> +
> +/* BIOS => Pcode commands */
> +#define IPC_BIOS_PUNIT_CMD_ZERO			(IPC_BIOS_PUNIT_CMD_BASE + 0x00)
> +#define IPC_BIOS_PUNIT_CMD_VR_INTERFACE		(IPC_BIOS_PUNIT_CMD_BASE + 0x01)
> +#define IPC_BIOS_PUNIT_CMD_READ_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x02)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x03)
> +#define IPC_BIOS_PUNIT_CMD_READ_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x04)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x05)
> +#define IPC_BIOS_PUNIT_CMD_READ_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x06)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x07)
> +#define IPC_BIOS_PUNIT_CMD_TRIGGER_VDD_RAM	(IPC_BIOS_PUNIT_CMD_BASE + 0x08)
> +#define IPC_BIOS_PUNIT_CMD_READ_TELE_INFO	(IPC_BIOS_PUNIT_CMD_BASE + 0x09)
> +#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0a)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE_CTRL \
> +						(IPC_BIOS_PUNIT_CMD_BASE + 0x0b)
> +#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0c)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT_CTRL \
> +						(IPC_BIOS_PUNIT_CMD_BASE + 0x0d)
> +#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0e)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0f)
> +#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x10)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x11)
> +#define IPC_BIOS_PUNIT_CMD_READ_MODULE_TEMP	(IPC_BIOS_PUNIT_CMD_BASE + 0x12)
> +#define IPC_BIOS_PUNIT_CMD_RESERVED		(IPC_BIOS_PUNIT_CMD_BASE + 0x13)
> +#define IPC_BIOS_PUNIT_CMD_READ_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x14)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x15)
> +#define IPC_BIOS_PUNIT_CMD_READ_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x16)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x17)
> +#define IPC_BIOS_PUNIT_CMD_READ_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x18)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x19)
> +#define IPC_BIOS_PUNIT_CMD_READ_FM_SOC_TEMP_THRESH \
> +						(IPC_BIOS_PUNIT_CMD_BASE + 0x1a)
> +#define IPC_BIOS_PUNIT_CMD_WRITE_FM_SOC_TEMP_THRESH \
> +						(IPC_BIOS_PUNIT_CMD_BASE + 0x1b)
> +
> +/*GT Driver => Pcode commands*/
> +#define IPC_GTD_PUNIT_CMD_ZERO			(IPC_GTD_PUNIT_CMD_BASE + 0x00)
> +#define IPC_GTD_PUNIT_CMD_CONFIG		(IPC_GTD_PUNIT_CMD_BASE + 0x01)
> +#define IPC_GTD_PUNIT_CMD_READ_ICCP_LIC_CDYN_SCAL \
> +						(IPC_GTD_PUNIT_CMD_BASE + 0x02)
> +#define IPC_GTD_PUNIT_CMD_WRITE_ICCP_LIC_CDYN_SCAL \
> +						(IPC_GTD_PUNIT_CMD_BASE + 0x03)
> +#define IPC_GTD_PUNIT_CMD_GET_WM_VAL		(IPC_GTD_PUNIT_CMD_BASE + 0x06)
> +#define IPC_GTD_PUNIT_CMD_WRITE_CONFIG_WISHREQ	(IPC_GTD_PUNIT_CMD_BASE + 0x07)
> +#define IPC_GTD_PUNIT_CMD_READ_REQ_DUTY_CYCLE	(IPC_GTD_PUNIT_CMD_BASE + 0x16)
> +#define IPC_GTD_PUNIT_CMD_DIS_VOL_FREQ_CHANGE_REQUEST \
> +						(IPC_GTD_PUNIT_CMD_BASE + 0x17)
> +#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_CTRL	(IPC_GTD_PUNIT_CMD_BASE + 0x1a)
> +#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_TUNING \
> +						(IPC_GTD_PUNIT_CMD_BASE + 0x1c)
> +
> +/* ISP Driver => Pcode commands */
> +#define IPC_ISPD_PUNIT_CMD_ZERO			(IPC_ISPD_PUNIT_CMD_BASE + 0x00)
> +#define IPC_ISPD_PUNIT_CMD_CONFIG		(IPC_ISPD_PUNIT_CMD_BASE + 0x01)
> +#define IPC_ISPD_PUNIT_CMD_GET_ISP_LTR_VAL	(IPC_ISPD_PUNIT_CMD_BASE + 0x02)
> +#define IPC_ISPD_PUNIT_CMD_ACCESS_IU_FREQ_BOUNDS \
> +						(IPC_ISPD_PUNIT_CMD_BASE + 0x03)
> +#define IPC_ISPD_PUNIT_CMD_READ_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x04)
> +#define IPC_ISPD_PUNIT_CMD_WRITE_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x05)
> +#define IPC_ISPD_PUNIT_CMD_MAX			(IPC_ISPD_PUNIT_CMD_BASE + 0x06)
> +
> +/* Error codes */
> +#define IPC_ERR_SUCCESS				0
> +#define IPC_ERR_INVALID_CMD			1
> +#define IPC_ERR_INVALID_PARAMETER		2
> +#define IPC_ERR_CMD_TIMEOUT			3
> +#define IPC_ERR_CMD_LOCKED			4
> +#define IPC_ERR_INVALID_VR_ID			5
> +#define IPC_ERR_VR_ERR				6
> +
> +#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> +
> +int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
> +int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
> +
> +#else
> +
> +static intline int intel_punit_ipc_simple_command(int cmd,
> +						  int para1, int para2);
> +{
> +	return -EINVAL;
> +}
> +
> +static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
> +					  u32 *in, u32 *out);
> +{
> +	return -EINVAL;
> +}
> +
> +#endif /*CONFIG_INTEL_PUNIT_IPC*/
> +
> +#endif
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 346f1fd..42ada9b 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -891,4 +891,10 @@ config INTEL_PMC_IPC
>  	The PMC is an ARC processor which defines IPC commands for communication
>  	with other entities in the CPU.
>  
> +config INTEL_PUNIT_IPC
> +	tristate "Intel PUNIT IPC Driver"
> +	default y
> +	---help---
> +	  IPC is used to bridge the communications between kernel and PUNIT
> +
>  endif # X86_PLATFORM_DEVICES
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 1051372..eea765f 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -59,3 +59,4 @@ obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
>  obj-$(CONFIG_PVPANIC)           += pvpanic.o
>  obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
>  obj-$(CONFIG_INTEL_PMC_IPC)	+= intel_pmc_ipc.o
> +obj-$(CONFIG_INTEL_PUNIT_IPC)	+= intel_punit_ipc.o
> diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
> new file mode 100644
> index 0000000..78cb794
> --- /dev/null
> +++ b/drivers/platform/x86/intel_punit_ipc.c
> @@ -0,0 +1,388 @@
> +/*
> + * intel_punit_ipc.c: Driver for the Intel Punit Mailbox IPC mechanism
> + *
> + * (C) Copyright 2015 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * The heart of the Punit is the Foxton microcontroller and its firmware,
> + * which provide mailbox interface for power management usage.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/pm.h>
> +#include <linux/acpi.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/bitops.h>
> +#include <linux/atomic.h>
> +#include <linux/notifier.h>
> +#include <linux/suspend.h>
> +#include <linux/platform_device.h>
> +#include <asm/intel_punit_ipc.h>
> +
> +/* Mailbox registers */
> +#define MAILBOX_DATA_LOW		0x0
> +#define MAILBOX_INTERFACE		0x4
> +#define		CMD_RUN			(1 << 31)
> +#define		CMD_ERRCODE_MASK	0xFF
> +#define		CMD_PARA1_SHIFT		8
> +#define		CMD_PARA2_SHIFT		16
> +#define		CMD_MASK		0xFF
> +#define MAILBOX_DATA_HIGH		0x8
> +
> +#define MAILBOX_REGISTER_SPACE		0x10
> +
> +#define CMD_TIMEOUT_SECONDS		3
> +
> +/* Three external mailbox */
> +enum mailbox_type {
> +	BIOS_MAILBOX,
> +	GTDRIVER_MAILBOX,
> +	ISPDRIVER_MAILBOX,
> +	RESERVED_MAILBOX,
> +};
> +
> +struct intel_punit_ipc_controller {
> +	struct platform_device *pdev;
> +	struct mutex lock;
> +	void __iomem *base[RESERVED_MAILBOX];
> +	struct completion cmd_complete;
> +	int irq;
> +
> +	int cmd;
> +	enum mailbox_type type;
> +};
> +
> +static char *ipc_err_sources[] = {
> +	[IPC_ERR_SUCCESS] =
> +		"no error",
> +	[IPC_ERR_INVALID_CMD] =
> +		"invalid command",
> +	[IPC_ERR_INVALID_PARAMETER] =
> +		"invalid parameter",
> +	[IPC_ERR_CMD_TIMEOUT] =
> +		"command timeout",
> +	[IPC_ERR_CMD_LOCKED] =
> +		"command locked",
> +	[IPC_ERR_INVALID_VR_ID] =
> +		"invalid vr id",
> +	[IPC_ERR_VR_ERR] =
> +		"vr error",
> +};
> +
> +static struct intel_punit_ipc_controller ipcdev;
> +
> +static inline u32 ipc_read_status(void)
> +{
> +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
> +}
> +
> +static inline void ipc_write_cmd(u32 cmd)
> +{
> +	writel(cmd, ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
> +}
> +
> +static inline u32 ipc_read_data_low(void)
> +{
> +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
> +}
> +
> +static inline u32 ipc_read_data_high(void)
> +{
> +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
> +}
> +
> +static inline void ipc_write_data_low(u32 data)
> +{
> +	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
> +}
> +
> +static inline void ipc_write_data_high(u32 data)
> +{
> +	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
> +}
> +
> +static int intel_punit_init_cmd(u32 cmd)
> +{
> +	cmd &= CMD_MASK;
> +	if (cmd < IPC_BIOS_PUNIT_CMD_BASE)
> +		return -EINVAL;
> +	else if (cmd < IPC_GTD_PUNIT_CMD_BASE)
> +		ipcdev.type = BIOS_MAILBOX;
> +	else if (cmd < IPC_ISPD_PUNIT_CMD_BASE)
> +		ipcdev.type = GTDRIVER_MAILBOX;
> +	else if (cmd < IPC_ISPD_PUNIT_CMD_MAX)
> +		ipcdev.type = ISPDRIVER_MAILBOX;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int intel_punit_ipc_send_command(u32 cmd)
> +{
> +	int ret;
> +
> +	ret = intel_punit_init_cmd(cmd);
> +	if (ret)
> +		return ret;
> +	ipcdev.cmd = cmd;
> +	reinit_completion(&ipcdev.cmd_complete);
> +	ipc_write_cmd(cmd);
> +	return 0;
> +}
> +
> +static int intel_punit_ipc_check_status(void)
> +{
> +	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> +	int ret = 0;
> +	int status;
> +	int errcode;
> +
> +	if (ipcdev.irq) {
> +		if (0 == wait_for_completion_timeout(
> +				&ipcdev.cmd_complete,
> +				CMD_TIMEOUT_SECONDS * HZ)) {
> +			dev_err(&ipcdev.pdev->dev,
> +				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
> +			return -ETIMEDOUT;
> +		}
> +	} else {
> +		while ((ipc_read_status() & CMD_RUN) && --loops)
> +			udelay(1);
> +		if (!loops) {
> +			dev_err(&ipcdev.pdev->dev,
> +				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	status = ipc_read_status();
> +	errcode = status & CMD_ERRCODE_MASK;
> +	if (errcode) {
> +		ret = -EIO;
> +		if (errcode < ARRAY_SIZE(ipc_err_sources))
> +			dev_err(&ipcdev.pdev->dev,
> +				"IPC failed: %s, IPC_STS=0x%x, IPC_CMD=0x%x\n",
> +				ipc_err_sources[errcode], status, ipcdev.cmd);
> +		else
> +			dev_err(&ipcdev.pdev->dev,
> +				"IPC failed: unknown err,STS=0x%x, CMD=0x%x\n",
> +				status, ipcdev.cmd);
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * intel_punit_ipc_simple_command() - Simple IPC command
> + * @cmd:	IPC command code.
> + * @para1:	First 8bit parameter, set 0 if invalid.
> + * @para2:	Second 8bit parameter, set 0 if invalid.
> + *
> + * Send a IPC command to Punit when there is no data transaction
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
> +{
> +	int ret;
> +
> +	mutex_lock(&ipcdev.lock);
> +	ret = intel_punit_ipc_send_command(CMD_RUN |
> +					   para2 << CMD_PARA2_SHIFT |
> +					   para1 << CMD_PARA1_SHIFT | cmd);
> +	if (ret)
> +		goto out;
> +	ret = intel_punit_ipc_check_status();
> +out:
> +	mutex_unlock(&ipcdev.lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> +
> +/**
> + * intel_punit_ipc_command() - IPC command with data and pointers
> + * @cmd:	IPC command code.
> + * @para1:	First 8bit parameter, set 0 if invalid.
> + * @para2:	Second 8bit parameter, set 0 if invalid.
> + * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
> + * @out:	Output data.
> + *
> + * Send a IPC command to Punit with data transaction
> + *
> + * Return:	an IPC error code or 0 on success.
> + */
> +int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
> +{
> +	int ret;
> +
> +	mutex_lock(&ipcdev.lock);
> +	ipc_write_data_low(*in);
> +	if (ipcdev.type == GTDRIVER_MAILBOX ||
> +			ipcdev.type == ISPDRIVER_MAILBOX) {
> +		in++;
> +		ipc_write_data_high(*in);
> +	}
> +	ret = intel_punit_ipc_send_command(CMD_RUN |
> +					   para2 << CMD_PARA2_SHIFT |
> +					   para1 << CMD_PARA1_SHIFT | cmd);
> +	if (ret)
> +		goto out;
> +	ret = intel_punit_ipc_check_status();
> +	*out = ipc_read_data_low();
> +	if (ipcdev.type == GTDRIVER_MAILBOX ||
> +			ipcdev.type == ISPDRIVER_MAILBOX) {
> +		out++;
> +		*out = ipc_read_data_high();
> +	}
> +out:
> +	mutex_unlock(&ipcdev.lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> +
> +static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> +{
> +	complete(&ipcdev.cmd_complete);
> +	return IRQ_HANDLED;
> +}
> +
> +static int intel_punit_get_bars(struct platform_device *pdev)
> +{
> +	struct resource *res0, *res1;
> +	void __iomem *addr;
> +	int size;
> +	int ret;
> +
> +	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res0) {
> +		dev_err(&pdev->dev, "Failed to get iomem resource\n");
> +		return -EINVAL;
> +	}
> +	size = resource_size(res0);
> +	if (!request_mem_region(res0->start, size, pdev->name)) {
> +		dev_err(&pdev->dev, "Failed to request iomem resouce\n");
> +		return -EBUSY;
> +	}
> +
> +	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!res1) {
> +		dev_err(&pdev->dev, "Failed to get iomem resource1\n");
> +		return -EINVAL;
> +	}
> +	size = resource_size(res1);
> +	if (!request_mem_region(res1->start, size, pdev->name)) {
> +		dev_err(&pdev->dev, "Failed to request iomem resouce1\n");
> +		ret = -EBUSY;
> +		goto err_res1;
> +	}
> +
> +	addr = ioremap_nocache(res0->start,
> +			       resource_size(res0) + resource_size(res1));
> +	if (!addr) {
> +		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
> +		ret = -ENOMEM;
> +		goto err_map;
> +	}
> +	ipcdev.base[BIOS_MAILBOX] = addr;
> +	addr += MAILBOX_REGISTER_SPACE;
> +	ipcdev.base[GTDRIVER_MAILBOX] = addr;
> +	addr += MAILBOX_REGISTER_SPACE;
> +	ipcdev.base[ISPDRIVER_MAILBOX] = addr;
> +
> +	return 0;
> +
> +err_map:
> +	release_mem_region(res1->start, resource_size(res1));
> +err_res1:
> +	release_mem_region(res0->start, resource_size(res0));
> +	return ret;
> +}
> +
> +static int intel_punit_ipc_probe(struct platform_device *pdev)
> +{
> +	int irq;
> +	int ret;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		ipcdev.irq = 0;
> +		dev_warn(&pdev->dev, "Could not get irq number\n");
> +	} else {
> +		if (request_irq(irq, intel_punit_ioc, IRQF_NO_SUSPEND,
> +				"intel_punit_ipc", &ipcdev)) {
> +			dev_err(&pdev->dev, "request irq %d\n", irq);
> +			return -EBUSY;
> +		}
> +		ipcdev.irq = irq;
> +	}
> +
> +	ret = intel_punit_get_bars(pdev);
> +	if (ret) {
> +		if (ipcdev.irq)
> +			free_irq(ipcdev.irq, &ipcdev);
> +		return ret;
> +	}
> +
> +	ipcdev.pdev = pdev;
> +	mutex_init(&ipcdev.lock);
> +	init_completion(&ipcdev.cmd_complete);
> +	return ret;
> +}
> +
> +static int intel_punit_ipc_remove(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +
> +	if (ipcdev.irq)
> +		free_irq(ipcdev.irq, &ipcdev);
> +	iounmap(ipcdev.base[BIOS_MAILBOX]);
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res)
> +		release_mem_region(res->start, resource_size(res));
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (res)
> +		release_mem_region(res->start, resource_size(res));
> +	return 0;
> +}
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id punit_ipc_acpi_ids[] = {
> +	{"INT34D4", 0}
> +};
> +#endif
> +
> +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),
> +	},
> +};
> +
> +static int __init intel_punit_ipc_init(void)
> +{
> +	return platform_driver_register(&intel_punit_ipc_driver);
> +}
> +
> +static void __exit intel_punit_ipc_exit(void)
> +{
> +	platform_driver_unregister(&intel_punit_ipc_driver);
> +}
> +
> +MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
> +MODULE_DESCRIPTION("Intel Punit IPC driver");
> +MODULE_LICENSE("GPL");
> +
> +/* Some modules are dependent on this, so init earlier */
> +fs_initcall(intel_punit_ipc_init);
> +module_exit(intel_punit_ipc_exit);
> -- 
> 1.8.3.2
> 
>
Mika Westerberg Aug. 7, 2015, 12:29 p.m. UTC | #2
On Wed, Aug 05, 2015 at 02:25:13PM -0700, Darren Hart wrote:
> On Fri, Jul 31, 2015 at 11:58:56PM +0800, Qipeng Zha wrote:
> > This driver provides support for Punit mailbox IPC on Intel platforms.
> > The heart of the Punit is the Foxton microcontroller and its firmware,
> > which provide mailbox interface for power management usage.
> > 
> 
> Mika, Andriy:
> 
> I'm traveling over the next few days and could use some help ensuring this new
> driver receives some additional review. If you can, I'd appreciate your
> thoughts.

Sure, no problem.

> > Signed-off-by: Qipeng Zha <qipeng.zha@intel.com>
> > ---
> >  arch/x86/include/asm/intel_punit_ipc.h | 101 +++++++++
> >  drivers/platform/x86/Kconfig           |   6 +
> >  drivers/platform/x86/Makefile          |   1 +
> >  drivers/platform/x86/intel_punit_ipc.c | 388 +++++++++++++++++++++++++++++++++
> >  4 files changed, 496 insertions(+)
> >  create mode 100644 arch/x86/include/asm/intel_punit_ipc.h
> >  create mode 100644 drivers/platform/x86/intel_punit_ipc.c
> > 
> > diff --git a/arch/x86/include/asm/intel_punit_ipc.h b/arch/x86/include/asm/intel_punit_ipc.h
> > new file mode 100644
> > index 0000000..08e3e14
> > --- /dev/null
> > +++ b/arch/x86/include/asm/intel_punit_ipc.h
> > @@ -0,0 +1,101 @@
> > +#ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
> > +#define  _ASM_X86_INTEL_PUNIT_IPC_H_
> > +
> > +/* Commands supported on GLM core, are handled by different bars,
> > + * unify these commands together here
> > + */
> > +#define IPC_BIOS_PUNIT_CMD_BASE				0x00
> > +#define IPC_GTD_PUNIT_CMD_BASE				0x20
> > +#define IPC_ISPD_PUNIT_CMD_BASE				0x40
> > +
> > +/* BIOS => Pcode commands */
> > +#define IPC_BIOS_PUNIT_CMD_ZERO			(IPC_BIOS_PUNIT_CMD_BASE + 0x00)
> > +#define IPC_BIOS_PUNIT_CMD_VR_INTERFACE		(IPC_BIOS_PUNIT_CMD_BASE + 0x01)
> > +#define IPC_BIOS_PUNIT_CMD_READ_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x02)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x03)
> > +#define IPC_BIOS_PUNIT_CMD_READ_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x04)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x05)
> > +#define IPC_BIOS_PUNIT_CMD_READ_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x06)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x07)
> > +#define IPC_BIOS_PUNIT_CMD_TRIGGER_VDD_RAM	(IPC_BIOS_PUNIT_CMD_BASE + 0x08)
> > +#define IPC_BIOS_PUNIT_CMD_READ_TELE_INFO	(IPC_BIOS_PUNIT_CMD_BASE + 0x09)
> > +#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0a)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE_CTRL \
> > +						(IPC_BIOS_PUNIT_CMD_BASE + 0x0b)
> > +#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0c)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT_CTRL \
> > +						(IPC_BIOS_PUNIT_CMD_BASE + 0x0d)
> > +#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0e)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0f)
> > +#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x10)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x11)
> > +#define IPC_BIOS_PUNIT_CMD_READ_MODULE_TEMP	(IPC_BIOS_PUNIT_CMD_BASE + 0x12)
> > +#define IPC_BIOS_PUNIT_CMD_RESERVED		(IPC_BIOS_PUNIT_CMD_BASE + 0x13)
> > +#define IPC_BIOS_PUNIT_CMD_READ_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x14)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x15)
> > +#define IPC_BIOS_PUNIT_CMD_READ_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x16)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x17)
> > +#define IPC_BIOS_PUNIT_CMD_READ_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x18)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x19)
> > +#define IPC_BIOS_PUNIT_CMD_READ_FM_SOC_TEMP_THRESH \
> > +						(IPC_BIOS_PUNIT_CMD_BASE + 0x1a)
> > +#define IPC_BIOS_PUNIT_CMD_WRITE_FM_SOC_TEMP_THRESH \
> > +						(IPC_BIOS_PUNIT_CMD_BASE + 0x1b)
> > +
> > +/*GT Driver => Pcode commands*/
> > +#define IPC_GTD_PUNIT_CMD_ZERO			(IPC_GTD_PUNIT_CMD_BASE + 0x00)
> > +#define IPC_GTD_PUNIT_CMD_CONFIG		(IPC_GTD_PUNIT_CMD_BASE + 0x01)
> > +#define IPC_GTD_PUNIT_CMD_READ_ICCP_LIC_CDYN_SCAL \
> > +						(IPC_GTD_PUNIT_CMD_BASE + 0x02)
> > +#define IPC_GTD_PUNIT_CMD_WRITE_ICCP_LIC_CDYN_SCAL \
> > +						(IPC_GTD_PUNIT_CMD_BASE + 0x03)
> > +#define IPC_GTD_PUNIT_CMD_GET_WM_VAL		(IPC_GTD_PUNIT_CMD_BASE + 0x06)
> > +#define IPC_GTD_PUNIT_CMD_WRITE_CONFIG_WISHREQ	(IPC_GTD_PUNIT_CMD_BASE + 0x07)
> > +#define IPC_GTD_PUNIT_CMD_READ_REQ_DUTY_CYCLE	(IPC_GTD_PUNIT_CMD_BASE + 0x16)
> > +#define IPC_GTD_PUNIT_CMD_DIS_VOL_FREQ_CHANGE_REQUEST \
> > +						(IPC_GTD_PUNIT_CMD_BASE + 0x17)
> > +#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_CTRL	(IPC_GTD_PUNIT_CMD_BASE + 0x1a)
> > +#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_TUNING \
> > +						(IPC_GTD_PUNIT_CMD_BASE + 0x1c)
> > +
> > +/* ISP Driver => Pcode commands */
> > +#define IPC_ISPD_PUNIT_CMD_ZERO			(IPC_ISPD_PUNIT_CMD_BASE + 0x00)
> > +#define IPC_ISPD_PUNIT_CMD_CONFIG		(IPC_ISPD_PUNIT_CMD_BASE + 0x01)
> > +#define IPC_ISPD_PUNIT_CMD_GET_ISP_LTR_VAL	(IPC_ISPD_PUNIT_CMD_BASE + 0x02)
> > +#define IPC_ISPD_PUNIT_CMD_ACCESS_IU_FREQ_BOUNDS \
> > +						(IPC_ISPD_PUNIT_CMD_BASE + 0x03)
> > +#define IPC_ISPD_PUNIT_CMD_READ_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x04)
> > +#define IPC_ISPD_PUNIT_CMD_WRITE_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x05)
> > +#define IPC_ISPD_PUNIT_CMD_MAX			(IPC_ISPD_PUNIT_CMD_BASE + 0x06)
> > +
> > +/* Error codes */
> > +#define IPC_ERR_SUCCESS				0
> > +#define IPC_ERR_INVALID_CMD			1
> > +#define IPC_ERR_INVALID_PARAMETER		2
> > +#define IPC_ERR_CMD_TIMEOUT			3
> > +#define IPC_ERR_CMD_LOCKED			4
> > +#define IPC_ERR_INVALID_VR_ID			5
> > +#define IPC_ERR_VR_ERR				6
> > +
> > +#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
> > +
> > +int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
> > +int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);

So is there going to be in-kernel user for this driver?

> > +
> > +#else
> > +
> > +static intline int intel_punit_ipc_simple_command(int cmd,
> > +						  int para1, int para2);
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
> > +					  u32 *in, u32 *out);
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +#endif /*CONFIG_INTEL_PUNIT_IPC*/
> > +
> > +#endif
> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > index 346f1fd..42ada9b 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -891,4 +891,10 @@ config INTEL_PMC_IPC
> >  	The PMC is an ARC processor which defines IPC commands for communication
> >  	with other entities in the CPU.
> >  
> > +config INTEL_PUNIT_IPC
> > +	tristate "Intel PUNIT IPC Driver"
> > +	default y

I'm sure it does not need to be enabled by default.

> > +	---help---
> > +	  IPC is used to bridge the communications between kernel and PUNIT
> > +
> >  endif # X86_PLATFORM_DEVICES
> > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> > index 1051372..eea765f 100644
> > --- a/drivers/platform/x86/Makefile
> > +++ b/drivers/platform/x86/Makefile
> > @@ -59,3 +59,4 @@ obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
> >  obj-$(CONFIG_PVPANIC)           += pvpanic.o
> >  obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
> >  obj-$(CONFIG_INTEL_PMC_IPC)	+= intel_pmc_ipc.o
> > +obj-$(CONFIG_INTEL_PUNIT_IPC)	+= intel_punit_ipc.o
> > diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
> > new file mode 100644
> > index 0000000..78cb794
> > --- /dev/null
> > +++ b/drivers/platform/x86/intel_punit_ipc.c
> > @@ -0,0 +1,388 @@
> > +/*
> > + * intel_punit_ipc.c: Driver for the Intel Punit Mailbox IPC mechanism

Drop the file name here.

> > + *
> > + * (C) Copyright 2015 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * The heart of the Punit is the Foxton microcontroller and its firmware,
> > + * which provide mailbox interface for power management usage.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/init.h>
> > +#include <linux/device.h>
> > +#include <linux/pm.h>
> > +#include <linux/acpi.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/kernel.h>
> > +#include <linux/bitops.h>
> > +#include <linux/atomic.h>
> > +#include <linux/notifier.h>
> > +#include <linux/suspend.h>
> > +#include <linux/platform_device.h>

Are you sure you need all the above headers?

> > +#include <asm/intel_punit_ipc.h>
> > +
> > +/* Mailbox registers */
> > +#define MAILBOX_DATA_LOW		0x0
> > +#define MAILBOX_INTERFACE		0x4
> > +#define		CMD_RUN			(1 << 31)
> > +#define		CMD_ERRCODE_MASK	0xFF
> > +#define		CMD_PARA1_SHIFT		8
> > +#define		CMD_PARA2_SHIFT		16
> > +#define		CMD_MASK		0xFF
> > +#define MAILBOX_DATA_HIGH		0x8
> > +
> > +#define MAILBOX_REGISTER_SPACE		0x10
> > +
> > +#define CMD_TIMEOUT_SECONDS		3
> > +
> > +/* Three external mailbox */
> > +enum mailbox_type {
> > +	BIOS_MAILBOX,
> > +	GTDRIVER_MAILBOX,
> > +	ISPDRIVER_MAILBOX,
> > +	RESERVED_MAILBOX,
> > +};
> > +
> > +struct intel_punit_ipc_controller {
> > +	struct platform_device *pdev;
> > +	struct mutex lock;
> > +	void __iomem *base[RESERVED_MAILBOX];
> > +	struct completion cmd_complete;
> > +	int irq;
> > +
> > +	int cmd;
> > +	enum mailbox_type type;
> > +};
> > +
> > +static char *ipc_err_sources[] = {
> > +	[IPC_ERR_SUCCESS] =
> > +		"no error",
> > +	[IPC_ERR_INVALID_CMD] =
> > +		"invalid command",
> > +	[IPC_ERR_INVALID_PARAMETER] =
> > +		"invalid parameter",
> > +	[IPC_ERR_CMD_TIMEOUT] =
> > +		"command timeout",
> > +	[IPC_ERR_CMD_LOCKED] =
> > +		"command locked",
> > +	[IPC_ERR_INVALID_VR_ID] =
> > +		"invalid vr id",
> > +	[IPC_ERR_VR_ERR] =
> > +		"vr error",
> > +};
> > +
> > +static struct intel_punit_ipc_controller ipcdev;

Why not to allocate the private structure at probe time?

> > +
> > +static inline u32 ipc_read_status(void)
> > +{
> > +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
> > +}
> > +
> > +static inline void ipc_write_cmd(u32 cmd)
> > +{
> > +	writel(cmd, ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
> > +}
> > +
> > +static inline u32 ipc_read_data_low(void)
> > +{
> > +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
> > +}
> > +
> > +static inline u32 ipc_read_data_high(void)
> > +{
> > +	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
> > +}
> > +
> > +static inline void ipc_write_data_low(u32 data)
> > +{
> > +	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
> > +}
> > +
> > +static inline void ipc_write_data_high(u32 data)
> > +{
> > +	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
> > +}
> > +
> > +static int intel_punit_init_cmd(u32 cmd)
> > +{
> > +	cmd &= CMD_MASK;
> > +	if (cmd < IPC_BIOS_PUNIT_CMD_BASE)
> > +		return -EINVAL;
> > +	else if (cmd < IPC_GTD_PUNIT_CMD_BASE)
> > +		ipcdev.type = BIOS_MAILBOX;
> > +	else if (cmd < IPC_ISPD_PUNIT_CMD_BASE)
> > +		ipcdev.type = GTDRIVER_MAILBOX;
> > +	else if (cmd < IPC_ISPD_PUNIT_CMD_MAX)
> > +		ipcdev.type = ISPDRIVER_MAILBOX;
> > +	else
> > +		return -EINVAL;
> > +
> > +	return 0;

	return -EINVAL and drop else


> > +}
> > +
> > +static int intel_punit_ipc_send_command(u32 cmd)
> > +{
> > +	int ret;
> > +
> > +	ret = intel_punit_init_cmd(cmd);
> > +	if (ret)
> > +		return ret;
> > +	ipcdev.cmd = cmd;
> > +	reinit_completion(&ipcdev.cmd_complete);
> > +	ipc_write_cmd(cmd);
> > +	return 0;
> > +}
> > +
> > +static int intel_punit_ipc_check_status(void)
> > +{
> > +	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
> > +	int ret = 0;
> > +	int status;
> > +	int errcode;
> > +
> > +	if (ipcdev.irq) {
> > +		if (0 == wait_for_completion_timeout(

		if (!wait_for_completion_timeout(

> > +				&ipcdev.cmd_complete,
> > +				CMD_TIMEOUT_SECONDS * HZ)) {
> > +			dev_err(&ipcdev.pdev->dev,
> > +				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
> > +			return -ETIMEDOUT;
> > +		}
> > +	} else {
> > +		while ((ipc_read_status() & CMD_RUN) && --loops)
> > +			udelay(1);
> > +		if (!loops) {
> > +			dev_err(&ipcdev.pdev->dev,
> > +				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
> > +			return -ETIMEDOUT;
> > +		}
> > +	}
> > +
> > +	status = ipc_read_status();
> > +	errcode = status & CMD_ERRCODE_MASK;
> > +	if (errcode) {
> > +		ret = -EIO;
> > +		if (errcode < ARRAY_SIZE(ipc_err_sources))
> > +			dev_err(&ipcdev.pdev->dev,
> > +				"IPC failed: %s, IPC_STS=0x%x, IPC_CMD=0x%x\n",
> > +				ipc_err_sources[errcode], status, ipcdev.cmd);
> > +		else
> > +			dev_err(&ipcdev.pdev->dev,
> > +				"IPC failed: unknown err,STS=0x%x, CMD=0x%x\n",
> > +				status, ipcdev.cmd);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * intel_punit_ipc_simple_command() - Simple IPC command
> > + * @cmd:	IPC command code.
> > + * @para1:	First 8bit parameter, set 0 if invalid.
> > + * @para2:	Second 8bit parameter, set 0 if invalid.
> > + *
> > + * Send a IPC command to Punit when there is no data transaction
> > + *
> > + * Return:	an IPC error code or 0 on success.
> > + */
> > +int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
> > +{
> > +	int ret;
> > +
> > +	mutex_lock(&ipcdev.lock);
> > +	ret = intel_punit_ipc_send_command(CMD_RUN |
> > +					   para2 << CMD_PARA2_SHIFT |
> > +					   para1 << CMD_PARA1_SHIFT | cmd);
> > +	if (ret)
> > +		goto out;
> > +	ret = intel_punit_ipc_check_status();
> > +out:
> > +	mutex_unlock(&ipcdev.lock);
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(intel_punit_ipc_simple_command);
> > +
> > +/**
> > + * intel_punit_ipc_command() - IPC command with data and pointers
> > + * @cmd:	IPC command code.
> > + * @para1:	First 8bit parameter, set 0 if invalid.
> > + * @para2:	Second 8bit parameter, set 0 if invalid.
> > + * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
> > + * @out:	Output data.
> > + *
> > + * Send a IPC command to Punit with data transaction
> > + *
> > + * Return:	an IPC error code or 0 on success.
> > + */
> > +int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
> > +{
> > +	int ret;
> > +
> > +	mutex_lock(&ipcdev.lock);
> > +	ipc_write_data_low(*in);
> > +	if (ipcdev.type == GTDRIVER_MAILBOX ||
> > +			ipcdev.type == ISPDRIVER_MAILBOX) {
> > +		in++;
> > +		ipc_write_data_high(*in);
> > +	}
> > +	ret = intel_punit_ipc_send_command(CMD_RUN |
> > +					   para2 << CMD_PARA2_SHIFT |
> > +					   para1 << CMD_PARA1_SHIFT | cmd);
> > +	if (ret)
> > +		goto out;
> > +	ret = intel_punit_ipc_check_status();
> > +	*out = ipc_read_data_low();
> > +	if (ipcdev.type == GTDRIVER_MAILBOX ||
> > +			ipcdev.type == ISPDRIVER_MAILBOX) {
> > +		out++;
> > +		*out = ipc_read_data_high();
> > +	}
> > +out:
> > +	mutex_unlock(&ipcdev.lock);
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
> > +
> > +static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
> > +{
> > +	complete(&ipcdev.cmd_complete);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int intel_punit_get_bars(struct platform_device *pdev)
> > +{
> > +	struct resource *res0, *res1;
> > +	void __iomem *addr;
> > +	int size;
> > +	int ret;
> > +
> > +	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (!res0) {
> > +		dev_err(&pdev->dev, "Failed to get iomem resource\n");
> > +		return -EINVAL;
> > +	}
> > +	size = resource_size(res0);
> > +	if (!request_mem_region(res0->start, size, pdev->name)) {
> > +		dev_err(&pdev->dev, "Failed to request iomem resouce\n");
> > +		return -EBUSY;
> > +	}

Why not use devm_ here, like:

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	addr = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(addr))
		return PTR_ERR(addr);

> > +
> > +	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +	if (!res1) {
> > +		dev_err(&pdev->dev, "Failed to get iomem resource1\n");
> > +		return -EINVAL;
> > +	}
> > +	size = resource_size(res1);
> > +	if (!request_mem_region(res1->start, size, pdev->name)) {
> > +		dev_err(&pdev->dev, "Failed to request iomem resouce1\n");
> > +		ret = -EBUSY;
> > +		goto err_res1;
> > +	}
> > +
> > +	addr = ioremap_nocache(res0->start,
> > +			       resource_size(res0) + resource_size(res1));
> > +	if (!addr) {
> > +		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
> > +		ret = -ENOMEM;
> > +		goto err_map;
> > +	}
> > +	ipcdev.base[BIOS_MAILBOX] = addr;
> > +	addr += MAILBOX_REGISTER_SPACE;
> > +	ipcdev.base[GTDRIVER_MAILBOX] = addr;
> > +	addr += MAILBOX_REGISTER_SPACE;
> > +	ipcdev.base[ISPDRIVER_MAILBOX] = addr;
> > +
> > +	return 0;
> > +
> > +err_map:
> > +	release_mem_region(res1->start, resource_size(res1));
> > +err_res1:
> > +	release_mem_region(res0->start, resource_size(res0));
> > +	return ret;
> > +}
> > +
> > +static int intel_punit_ipc_probe(struct platform_device *pdev)
> > +{
> > +	int irq;
> > +	int ret;

Looks better if you do

	int irq, ret;

> > +
> > +	irq = platform_get_irq(pdev, 0);
> > +	if (irq < 0) {
> > +		ipcdev.irq = 0;
> > +		dev_warn(&pdev->dev, "Could not get irq number\n");
> > +	} else {
> > +		if (request_irq(irq, intel_punit_ioc, IRQF_NO_SUSPEND,
> > +				"intel_punit_ipc", &ipcdev)) {
> > +			dev_err(&pdev->dev, "request irq %d\n", irq);
> > +			return -EBUSY;
> > +		}
> > +		ipcdev.irq = irq;
> > +	}

devm_request_irq()?

> > +
> > +	ret = intel_punit_get_bars(pdev);
> > +	if (ret) {
> > +		if (ipcdev.irq)
> > +			free_irq(ipcdev.irq, &ipcdev);

Then you don't need all this...

> > +		return ret;
> > +	}
> > +
> > +	ipcdev.pdev = pdev;
> > +	mutex_init(&ipcdev.lock);
> > +	init_completion(&ipcdev.cmd_complete);
> > +	return ret;
> > +}
> > +
> > +static int intel_punit_ipc_remove(struct platform_device *pdev)
> > +{
> > +	struct resource *res;
> > +
> > +	if (ipcdev.irq)
> > +		free_irq(ipcdev.irq, &ipcdev);
> > +	iounmap(ipcdev.base[BIOS_MAILBOX]);
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	if (res)
> > +		release_mem_region(res->start, resource_size(res));
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +	if (res)
> > +		release_mem_region(res->start, resource_size(res));

...nor this.

> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_ACPI
> > +static const struct acpi_device_id punit_ipc_acpi_ids[] = {
> > +	{"INT34D4", 0}
> > +};
> > +#endif
> > +
> > +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),
> > +	},
> > +};
> > +
> > +static int __init intel_punit_ipc_init(void)
> > +{
> > +	return platform_driver_register(&intel_punit_ipc_driver);
> > +}
> > +
> > +static void __exit intel_punit_ipc_exit(void)
> > +{
> > +	platform_driver_unregister(&intel_punit_ipc_driver);
> > +}
> > +
> > +MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
> > +MODULE_DESCRIPTION("Intel Punit IPC driver");
> > +MODULE_LICENSE("GPL");

GPLv2

> > +
> > +/* Some modules are dependent on this, so init earlier */
> > +fs_initcall(intel_punit_ipc_init);
> > +module_exit(intel_punit_ipc_exit);
> > -- 
> > 1.8.3.2
> > 
> > 
> 
> -- 
> Darren Hart
> Intel Open Source Technology Center
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko Aug. 10, 2015, 3:04 p.m. UTC | #3
W0NjOiBNYXR0IHRvIGRpc2N1c3MgaVRDTyB0aGluZyBmb3VuZCBpbiBvbmUgb2YgdGhlIGRyaXZl
cnNdDQoNCk9uIFdlZCwgMjAxNS0wOC0wNSBhdCAxNDoyNSAtMDcwMCwgRGFycmVuIEhhcnQgd3Jv
dGU6DQo+IE9uIEZyaSwgSnVsIDMxLCAyMDE1IGF0IDExOjU4OjU2UE0gKzA4MDAsIFFpcGVuZyBa
aGEgd3JvdGU6DQo+ID4gVGhpcyBkcml2ZXIgcHJvdmlkZXMgc3VwcG9ydCBmb3IgUHVuaXQgbWFp
bGJveCBJUEMgb24gSW50ZWwgDQo+ID4gcGxhdGZvcm1zLg0KPiA+IFRoZSBoZWFydCBvZiB0aGUg
UHVuaXQgaXMgdGhlIEZveHRvbiBtaWNyb2NvbnRyb2xsZXIgYW5kIGl0cyANCj4gPiBmaXJtd2Fy
ZSwNCj4gPiB3aGljaCBwcm92aWRlIG1haWxib3ggaW50ZXJmYWNlIGZvciBwb3dlciBtYW5hZ2Vt
ZW50IHVzYWdlLg0KPiA+IA0KPiANCj4gTWlrYSwgQW5kcml5Og0KPiANCj4gSSdtIHRyYXZlbGlu
ZyBvdmVyIHRoZSBuZXh0IGZldyBkYXlzIGFuZCBjb3VsZCB1c2Ugc29tZSBoZWxwIGVuc3VyaW5n
IA0KPiB0aGlzIG5ldw0KPiBkcml2ZXIgcmVjZWl2ZXMgc29tZSBhZGRpdGlvbmFsIHJldmlldy4g
SWYgeW91IGNhbiwgSSdkIGFwcHJlY2lhdGUgDQo+IHlvdXINCj4gdGhvdWdodHMuDQoNClllcywg
d2lsbCBkbyByaWdodCBub3cuDQoNCg0KRmlyc3Qgb2YgYWxsIHdlIGhhdmUgbWFueSB0aGluZ3Mg
aGVyZSBhbmQgdGhlcmUgcmVsYXRlZCB0byBwb3dlcg0KY29udHJvbCBkZXZpY2VzIG9uIHZhcmlv
dXMgSW50ZWwgaGFyZHdhcmUuDQoNCmFyY2gveDg2L3BsYXRmb3JtL2F0b20vcG1jX2F0b20uYw0K
YXJjaC94ODYvcGxhdGZvcm0vYXRvbS9wdW5pdF9hdG9tX2RlYnVnLmMNCmRyaXZlcnMvbWZkL2lu
dGVsX3NvY19wbWljX2NvcmUuYw0KZHJpdmVycy9wbGF0Zm9ybS94ODYvaW50ZWxfcG1jX2lwYy5j
DQpkcml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9wbWljX2dwaW8uYw0KDQpTbywgd2hhdCBJIHNl
ZSBpcyB0aGUgbWVuYWdlcmllIG9mIGRyaXZlcnMgc3ByZWFkIGluIHRoZSBzb3VyY2VzIHRyZWUu
DQoNCkFuZCBvbiB0b3Agb2YgdGhhdCB3ZSBoYXZlIElPU0YgbWFpbGJveCBpbXBsZW1lbnRhdGlv
bi4NCg0KU28sIHRoZSBtYWluIHF1ZXN0aW9ucyBhcmU6DQogLSBEb2VzIGFueSBvZiBhYm92ZSBj
b3JyZWxhdGUgd2l0aCB3aGF0IHlvdSBhcmUgZG9pbmcgaW4gdGhpcyBkcml2ZXI/DQogLSBJcyBQ
VW5pdCBhY2Nlc3NpYmxlIHZpYSBJT1NGIG1haWxib3g/DQogLSBXb3VsZCBpdCBiZSBnZW5lcmlj
IGRyaXZlciBmb3IgbWFueSBJbnRlbCBwbGF0Zm9ybXMgb3Igb25seSBmb3IgZmV3Pw0KDQpBbHNv
IHRoZSBuYW1lIG9mIHRoZSBtb2R1bGUgaXMgaW50ZWxfcHVuaXRfaXBjIHdoaWNoIGlzIHNvbWVo
b3cgdXNlZCBpbg0KaW50ZWxfcG1jX2lwYy5jLg0KDQpNYXR0LCBieSB0aGUgd2F5IGludGVsX3Bt
Y19pcGMuYyBtb2R1bGUgY3JlYXRlcyBpVENPIGRldmljZSAoTFBDIGJ1cykuDQpEb2VzIGl0IGxv
b2sgY29ycmVjdD8NCg0KPiANCj4gPiBTaWduZWQtb2ZmLWJ5OiBRaXBlbmcgWmhhIDxxaXBlbmcu
emhhQGludGVsLmNvbT4NCj4gPiAtLS0NCj4gPiAgYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxf
cHVuaXRfaXBjLmggfCAxMDEgKysrKysrKysrDQo+ID4gIGRyaXZlcnMvcGxhdGZvcm0veDg2L0tj
b25maWcgICAgICAgICAgIHwgICA2ICsNCj4gPiAgZHJpdmVycy9wbGF0Zm9ybS94ODYvTWFrZWZp
bGUgICAgICAgICAgfCAgIDEgKw0KPiA+ICBkcml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9wdW5p
dF9pcGMuYyB8IDM4OCANCj4gPiArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysNCj4g
PiAgNCBmaWxlcyBjaGFuZ2VkLCA0OTYgaW5zZXJ0aW9ucygrKQ0KPiA+ICBjcmVhdGUgbW9kZSAx
MDA2NDQgYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxfcHVuaXRfaXBjLmgNCj4gPiAgY3JlYXRl
IG1vZGUgMTAwNjQ0IGRyaXZlcnMvcGxhdGZvcm0veDg2L2ludGVsX3B1bml0X2lwYy5jDQo+ID4g
DQo+ID4gZGlmZiAtLWdpdCBhL2FyY2gveDg2L2luY2x1ZGUvYXNtL2ludGVsX3B1bml0X2lwYy5o
IA0KPiA+IGIvYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxfcHVuaXRfaXBjLmgNCj4gPiBuZXcg
ZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGluZGV4IDAwMDAwMDAuLjA4ZTNlMTQNCj4gPiAtLS0gL2Rl
di9udWxsDQo+ID4gKysrIGIvYXJjaC94ODYvaW5jbHVkZS9hc20vaW50ZWxfcHVuaXRfaXBjLmgN
Cj4gPiBAQCAtMCwwICsxLDEwMSBAQA0KPiA+ICsjaWZuZGVmIF9BU01fWDg2X0lOVEVMX1BVTklU
X0lQQ19IXw0KPiA+ICsjZGVmaW5lICBfQVNNX1g4Nl9JTlRFTF9QVU5JVF9JUENfSF8NCj4gPiAr
DQo+ID4gKy8qIENvbW1hbmRzIHN1cHBvcnRlZCBvbiBHTE0gY29yZSwgYXJlIGhhbmRsZWQgYnkg
ZGlmZmVyZW50IGJhcnMsDQo+ID4gKyAqIHVuaWZ5IHRoZXNlIGNvbW1hbmRzIHRvZ2V0aGVyIGhl
cmUNCj4gPiArICovDQo+ID4gKyNkZWZpbmUgSVBDX0JJT1NfUFVOSVRfQ01EX0JBU0UJCQkJMHgN
Cj4gPiAwMA0KPiA+ICsjZGVmaW5lIElQQ19HVERfUFVOSVRfQ01EX0JBU0UJCQkJMHgyMA0KPiA+
ICsjZGVmaW5lIElQQ19JU1BEX1BVTklUX0NNRF9CQVNFCQkJCTB4DQo+ID4gNDANCg0KV2hhdCBh
Ym91dCB1c2luZyBwcmVmaXggSVBDX1BVTklUXyBldmVyeXdoZXJlIGluIHRoaXMgZHJpdmVyPw0K
DQo+ID4gKw0KPiA+ICsvKiBCSU9TID0+IFBjb2RlIGNvbW1hbmRzICovDQo+ID4gKyNkZWZpbmUg
SVBDX0JJT1NfUFVOSVRfQ01EX1pFUk8JCQkoSVBDX0JJT1NfDQo+ID4gUFVOSVRfQ01EX0JBU0Ug
KyAweDAwKQ0KDQpDYW4gd2UganVzdCBwcm92aWRlIGEgcGxhaW4gbnVtYmVyIHBlciBlYWNoIGNv
bW1hbmQ/DQoNCk1vcmVvdmVyLCBkbyB3ZSBuZWVkIGFsbCBjb21tYW5kcyB0byBiZSBwcmVzZW50
IGluIHRoZSBmaXJzdCBwbGFjZT8NCkFueSB1c2VycyBmb3IgdGhlbSBhbGw/DQoNCj4gPiArI2Rl
ZmluZSBJUENfQklPU19QVU5JVF9DTURfVlJfSU5URVJGQUNFCQkoSVBDX0JJT1NfDQo+ID4gUFVO
SVRfQ01EX0JBU0UgKyAweDAxKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFE
X1BDUwkJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwMikNCj4gPiArI2RlZmlu
ZSBJUENfQklPU19QVU5JVF9DTURfV1JJVEVfUENTCQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBE
X0JBU0UgKyAweDAzKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFEX1BDVV9D
T05GSUcJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwNCkNCj4gPiArI2RlZmlu
ZSBJUENfQklPU19QVU5JVF9DTURfV1JJVEVfUENVX0NPTkZJRwkoSVBDX0JJT1NfUFVOSVRfQ00N
Cj4gPiBEX0JBU0UgKyAweDA1KQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFE
X1BMMV9TRVRUSU5HCShJUENfQklPU19QVU5JVF9DTQ0KPiA+IERfQkFTRSArIDB4MDYpDQo+ID4g
KyNkZWZpbmUgSVBDX0JJT1NfUFVOSVRfQ01EX1dSSVRFX1BMMV9TRVRUSU5HCShJUENfQklPU19Q
VU5JVF9DTQ0KPiA+IERfQkFTRSArIDB4MDcpDQo+ID4gKyNkZWZpbmUgSVBDX0JJT1NfUFVOSVRf
Q01EX1RSSUdHRVJfVkREX1JBTQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDA4
KQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFEX1RFTEVfSU5GTwkoSVBDX0JJ
T1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDA5KQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BV
TklUX0NNRF9SRUFEX1RFTEVfVFJBQ0VfQ1RSTAkoSVBDX0JJT1NfDQo+ID4gUFVOSVRfQ01EX0JB
U0UgKyAweDBhKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9XUklURV9URUxFX1RS
QUNFX0NUUkwgXA0KPiA+ICsJCQkJCQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAw
eDBiKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFEX1RFTEVfRVZFTlRfQ1RS
TAkoSVBDX0JJT1NfDQo+ID4gUFVOSVRfQ01EX0JBU0UgKyAweDBjKQ0KPiA+ICsjZGVmaW5lIElQ
Q19CSU9TX1BVTklUX0NNRF9XUklURV9URUxFX0VWRU5UX0NUUkwgXA0KPiA+ICsJCQkJCQkoSVBD
X0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDBkKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9T
X1BVTklUX0NNRF9SRUFEX1RFTEVfVFJBQ0UJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9CQVNF
ICsgMHgwZSkNCj4gPiArI2RlZmluZSBJUENfQklPU19QVU5JVF9DTURfV1JJVEVfVEVMRV9UUkFD
RQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDBmKQ0KPiA+ICsjZGVmaW5lIElQ
Q19CSU9TX1BVTklUX0NNRF9SRUFEX1RFTEVfRVZFTlQJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4g
RF9CQVNFICsgMHgxMCkNCj4gPiArI2RlZmluZSBJUENfQklPU19QVU5JVF9DTURfV1JJVEVfVEVM
RV9FVkVOVAkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDExKQ0KPiA+ICsjZGVm
aW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFEX01PRFVMRV9URU1QCShJUENfQklPU19QVU5JVF9D
TQ0KPiA+IERfQkFTRSArIDB4MTIpDQo+ID4gKyNkZWZpbmUgSVBDX0JJT1NfUFVOSVRfQ01EX1JF
U0VSVkVECQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAweDEzKQ0KPiA+ICsjZGVm
aW5lIElQQ19CSU9TX1BVTklUX0NNRF9SRUFEX1ZPTFRBR0VfT1ZFUgkoSVBDX0JJT1NfUFVOSVRf
Q00NCj4gPiBEX0JBU0UgKyAweDE0KQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9X
UklURV9WT0xUQUdFX09WRVIJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgxNSkN
Cj4gPiArI2RlZmluZSBJUENfQklPU19QVU5JVF9DTURfUkVBRF9SQVRJT19PVkVSCShJUENfQklP
U19QVU5JVF9DTQ0KPiA+IERfQkFTRSArIDB4MTYpDQo+ID4gKyNkZWZpbmUgSVBDX0JJT1NfUFVO
SVRfQ01EX1dSSVRFX1JBVElPX09WRVIJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9CQVNFICsg
MHgxNykNCj4gPiArI2RlZmluZSBJUENfQklPU19QVU5JVF9DTURfUkVBRF9WRl9HTF9DVFJMCShJ
UENfQklPU19QVU5JVF9DTQ0KPiA+IERfQkFTRSArIDB4MTgpDQo+ID4gKyNkZWZpbmUgSVBDX0JJ
T1NfUFVOSVRfQ01EX1dSSVRFX1ZGX0dMX0NUUkwJKElQQ19CSU9TX1BVTklUX0NNDQo+ID4gRF9C
QVNFICsgMHgxOSkNCj4gPiArI2RlZmluZSBJUENfQklPU19QVU5JVF9DTURfUkVBRF9GTV9TT0Nf
VEVNUF9USFJFU0ggXA0KPiA+ICsJCQkJCQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0Ug
KyAweDFhKQ0KPiA+ICsjZGVmaW5lIElQQ19CSU9TX1BVTklUX0NNRF9XUklURV9GTV9TT0NfVEVN
UF9USFJFU0ggXA0KPiA+ICsJCQkJCQkoSVBDX0JJT1NfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAw
eDFiKQ0KPiA+ICsNCj4gPiArLypHVCBEcml2ZXIgPT4gUGNvZGUgY29tbWFuZHMqLw0KPiA+ICsj
ZGVmaW5lIElQQ19HVERfUFVOSVRfQ01EX1pFUk8JCQkoSVBDX0dURF9QVU5JVF9DTUQNCj4gPiBf
QkFTRSArIDB4MDApDQo+ID4gKyNkZWZpbmUgSVBDX0dURF9QVU5JVF9DTURfQ09ORklHCQkoSVBD
X0dURF9QVU5JVF9DTUQNCj4gPiBfQkFTRSArIDB4MDEpDQo+ID4gKyNkZWZpbmUgSVBDX0dURF9Q
VU5JVF9DTURfUkVBRF9JQ0NQX0xJQ19DRFlOX1NDQUwgXA0KPiA+ICsJCQkJCQkoSVBDX0dURF9Q
VU5JVF9DTUQNCj4gPiBfQkFTRSArIDB4MDIpDQo+ID4gKyNkZWZpbmUgSVBDX0dURF9QVU5JVF9D
TURfV1JJVEVfSUNDUF9MSUNfQ0RZTl9TQ0FMIFwNCj4gPiArCQkJCQkJKElQQ19HVERfUFVOSVRf
Q01EDQo+ID4gX0JBU0UgKyAweDAzKQ0KPiA+ICsjZGVmaW5lIElQQ19HVERfUFVOSVRfQ01EX0dF
VF9XTV9WQUwJCShJUENfR1REX1BVTklUX0NNRA0KPiA+IF9CQVNFICsgMHgwNikNCj4gPiArI2Rl
ZmluZSBJUENfR1REX1BVTklUX0NNRF9XUklURV9DT05GSUdfV0lTSFJFUQkoSVBDX0dURF9QVU5J
VF9DTUQNCj4gPiBfQkFTRSArIDB4MDcpDQo+ID4gKyNkZWZpbmUgSVBDX0dURF9QVU5JVF9DTURf
UkVBRF9SRVFfRFVUWV9DWUNMRQkoSVBDX0dURF9QVU5JVF9DTUQNCj4gPiBfQkFTRSArIDB4MTYp
DQo+ID4gKyNkZWZpbmUgSVBDX0dURF9QVU5JVF9DTURfRElTX1ZPTF9GUkVRX0NIQU5HRV9SRVFV
RVNUIFwNCj4gPiArCQkJCQkJKElQQ19HVERfUFVOSVRfQ01EDQo+ID4gX0JBU0UgKyAweDE3KQ0K
PiA+ICsjZGVmaW5lIElQQ19HVERfUFVOSVRfQ01EX0RZTkFfRFVUWV9DWUNMRV9DVFJMCShJUENf
R1REX1BVTklUX0NNRA0KPiA+IF9CQVNFICsgMHgxYSkNCj4gPiArI2RlZmluZSBJUENfR1REX1BV
TklUX0NNRF9EWU5BX0RVVFlfQ1lDTEVfVFVOSU5HIFwNCj4gPiArCQkJCQkJKElQQ19HVERfUFVO
SVRfQ01EDQo+ID4gX0JBU0UgKyAweDFjKQ0KPiA+ICsNCj4gPiArLyogSVNQIERyaXZlciA9PiBQ
Y29kZSBjb21tYW5kcyAqLw0KPiA+ICsjZGVmaW5lIElQQ19JU1BEX1BVTklUX0NNRF9aRVJPCQkJ
KElQQ19JU1BEXw0KPiA+IFBVTklUX0NNRF9CQVNFICsgMHgwMCkNCj4gPiArI2RlZmluZSBJUENf
SVNQRF9QVU5JVF9DTURfQ09ORklHCQkoSVBDX0lTUERfUFVOSVRfQ00NCj4gPiBEX0JBU0UgKyAw
eDAxKQ0KPiA+ICsjZGVmaW5lIElQQ19JU1BEX1BVTklUX0NNRF9HRVRfSVNQX0xUUl9WQUwJKElQ
Q19JU1BEX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwMikNCj4gPiArI2RlZmluZSBJUENfSVNQ
RF9QVU5JVF9DTURfQUNDRVNTX0lVX0ZSRVFfQk9VTkRTIFwNCj4gPiArCQkJCQkJKElQQ19JU1BE
X1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwMykNCj4gPiArI2RlZmluZSBJUENfSVNQRF9QVU5J
VF9DTURfUkVBRF9DRFlOX0xFVkVMCShJUENfSVNQRF9QVU5JVF9DTQ0KPiA+IERfQkFTRSArIDB4
MDQpDQo+ID4gKyNkZWZpbmUgSVBDX0lTUERfUFVOSVRfQ01EX1dSSVRFX0NEWU5fTEVWRUwJKElQ
Q19JU1BEX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwNSkNCj4gPiArI2RlZmluZSBJUENfSVNQ
RF9QVU5JVF9DTURfTUFYCQkJKElQQ19JU1BEX1BVTklUX0NNDQo+ID4gRF9CQVNFICsgMHgwNikN
Cj4gPiArDQo+ID4gKy8qIEVycm9yIGNvZGVzICovDQo+ID4gKyNkZWZpbmUgSVBDX0VSUl9TVUND
RVNTCQkJCTANCj4gPiArI2RlZmluZSBJUENfRVJSX0lOVkFMSURfQ01ECQkJMQ0KPiA+ICsjZGVm
aW5lIElQQ19FUlJfSU5WQUxJRF9QQVJBTUVURVIJCTINCj4gPiArI2RlZmluZSBJUENfRVJSX0NN
RF9USU1FT1VUCQkJMw0KPiA+ICsjZGVmaW5lIElQQ19FUlJfQ01EX0xPQ0tFRAkJCTQNCj4gPiAr
I2RlZmluZSBJUENfRVJSX0lOVkFMSURfVlJfSUQJCQk1DQo+ID4gKyNkZWZpbmUgSVBDX0VSUl9W
Ul9FUlIJCQkJNg0KPiA+ICsNCj4gPiArI2lmIElTX0VOQUJMRUQoQ09ORklHX0lOVEVMX1BVTklU
X0lQQykNCj4gPiArDQo+ID4gK2ludCBpbnRlbF9wdW5pdF9pcGNfc2ltcGxlX2NvbW1hbmQoaW50
IGNtZCwgaW50IHBhcmExLCBpbnQgcGFyYTIpOw0KPiA+ICtpbnQgaW50ZWxfcHVuaXRfaXBjX2Nv
bW1hbmQodTMyIGNtZCwgdTMyIHBhcmExLCB1MzIgcGFyYTIsIHUzMiANCj4gPiAqaW4sIHUzMiAq
b3V0KTsNCj4gPiArDQo+ID4gKyNlbHNlDQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50bGluZSBpbnQg
aW50ZWxfcHVuaXRfaXBjX3NpbXBsZV9jb21tYW5kKGludCBjbWQsDQo+ID4gKwkJCQkJCSAgaW50
IHBhcmExLCBpbnQgDQoNCg0KU2VlbXMgd2FzIG5ldmVyIGNvbXBpbGVkIHdpdGggZGlzYWJsZWQg
b3B0aW9uLg0KDQo+ID4gcGFyYTIpOw0KPiA+ICt7DQo+ID4gKwlyZXR1cm4gLUVJTlZBTDsNCj4g
PiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGlubGluZSBpbnQgaW50ZWxfcHVuaXRfaXBjX2NvbW1h
bmQodTMyIGNtZCwgdTMyIHBhcmExLCB1MzIgDQo+ID4gcGFyYTIsDQo+ID4gKwkJCQkJICB1MzIg
KmluLCB1MzIgKm91dCk7DQo+ID4gK3sNCj4gPiArCXJldHVybiAtRUlOVkFMOw0KPiA+ICt9DQo+
ID4gKw0KPiA+ICsjZW5kaWYgLypDT05GSUdfSU5URUxfUFVOSVRfSVBDKi8NCj4gPiArDQo+ID4g
KyNlbmRpZg0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9LY29uZmlnIA0K
PiA+IGIvZHJpdmVycy9wbGF0Zm9ybS94ODYvS2NvbmZpZw0KPiA+IGluZGV4IDM0NmYxZmQuLjQy
YWRhOWIgMTAwNjQ0DQo+ID4gLS0tIGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvS2NvbmZpZw0KPiA+
ICsrKyBiL2RyaXZlcnMvcGxhdGZvcm0veDg2L0tjb25maWcNCj4gPiBAQCAtODkxLDQgKzg5MSwx
MCBAQCBjb25maWcgSU5URUxfUE1DX0lQQw0KPiA+ICAJVGhlIFBNQyBpcyBhbiBBUkMgcHJvY2Vz
c29yIHdoaWNoIGRlZmluZXMgSVBDIGNvbW1hbmRzIGZvciANCj4gPiBjb21tdW5pY2F0aW9uDQo+
ID4gIAl3aXRoIG90aGVyIGVudGl0aWVzIGluIHRoZSBDUFUuDQo+ID4gIA0KPiA+ICtjb25maWcg
SU5URUxfUFVOSVRfSVBDDQo+ID4gKwl0cmlzdGF0ZSAiSW50ZWwgUFVOSVQgSVBDIERyaXZlciIN
Cg0KUFVOSVQgLT4gUC1Vbml0ID8NCg0KPiA+ICsJZGVmYXVsdCB5DQo+ID4gKwktLS1oZWxwLS0t
DQo+ID4gKwkgIElQQyBpcyB1c2VkIHRvIGJyaWRnZSB0aGUgY29tbXVuaWNhdGlvbnMgYmV0d2Vl
biBrZXJuZWwgDQo+ID4gYW5kIFBVTklUDQoNCkRpdHRvLg0KDQo+ID4gKw0KPiA+ICBlbmRpZiAj
IFg4Nl9QTEFURk9STV9ERVZJQ0VTDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvcGxhdGZvcm0v
eDg2L01ha2VmaWxlIA0KPiA+IGIvZHJpdmVycy9wbGF0Zm9ybS94ODYvTWFrZWZpbGUNCj4gPiBp
bmRleCAxMDUxMzcyLi5lZWE3NjVmIDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvcGxhdGZvcm0v
eDg2L01ha2VmaWxlDQo+ID4gKysrIGIvZHJpdmVycy9wbGF0Zm9ybS94ODYvTWFrZWZpbGUNCj4g
PiBAQCAtNTksMyArNTksNCBAQCBvYmotJChDT05GSUdfSU5URUxfU01BUlRDT05ORUNUKQkrPSBp
bnRlbA0KPiA+IC1zbWFydGNvbm5lY3Qubw0KPiA+ICBvYmotJChDT05GSUdfUFZQQU5JQykgICAg
ICAgICAgICs9IHB2cGFuaWMubw0KPiA+ICBvYmotJChDT05GSUdfQUxJRU5XQVJFX1dNSSkJKz0g
YWxpZW53YXJlLXdtaS5vDQo+ID4gIG9iai0kKENPTkZJR19JTlRFTF9QTUNfSVBDKQkrPSBpbnRl
bF9wbWNfaXBjLm8NCj4gPiArb2JqLSQoQ09ORklHX0lOVEVMX1BVTklUX0lQQykJKz0gaW50ZWxf
cHVuaXRfaXBjLm8NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9wbGF0Zm9ybS94ODYvaW50ZWxf
cHVuaXRfaXBjLmMgDQo+ID4gYi9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9wdW5pdF9pcGMu
Yw0KPiA+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4gaW5kZXggMDAwMDAwMC4uNzhjYjc5NA0K
PiA+IC0tLSAvZGV2L251bGwNCj4gPiArKysgYi9kcml2ZXJzL3BsYXRmb3JtL3g4Ni9pbnRlbF9w
dW5pdF9pcGMuYw0KPiA+IEBAIC0wLDAgKzEsMzg4IEBADQo+ID4gKy8qDQo+ID4gKyAqIGludGVs
X3B1bml0X2lwYy5jOiBEcml2ZXIgZm9yIHRoZSBJbnRlbCBQdW5pdCBNYWlsYm94IElQQyANCj4g
PiBtZWNoYW5pc20NCj4gPiArICoNCj4gPiArICogKEMpIENvcHlyaWdodCAyMDE1IEludGVsIENv
cnBvcmF0aW9uDQo+ID4gKyAqDQo+ID4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJl
OyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgDQo+ID4gbW9kaWZ5DQo+ID4gKyAqIGl0
IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVyc2lv
biAyIA0KPiA+IGFzDQo+ID4gKyAqIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3Vu
ZGF0aW9uLg0KPiA+ICsgKg0KPiA+ICsgKiBUaGUgaGVhcnQgb2YgdGhlIFB1bml0IGlzIHRoZSBG
b3h0b24gbWljcm9jb250cm9sbGVyIGFuZCBpdHMgDQoNCg0KRGl0dG8uDQoNCj4gPiBmaXJtd2Fy
ZSwNCj4gPiArICogd2hpY2ggcHJvdmlkZSBtYWlsYm94IGludGVyZmFjZSBmb3IgcG93ZXIgbWFu
YWdlbWVudCB1c2FnZS4NCj4gPiArICovDQo+ID4gKw0KPiA+ICsjaW5jbHVkZSA8bGludXgvbW9k
dWxlLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9kZWxheS5oPg0KPiA+ICsjaW5jbHVkZSA8bGlu
dXgvZXJybm8uaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L2luaXQuaD4NCj4gPiArI2luY2x1ZGUg
PGxpbnV4L2RldmljZS5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvcG0uaD4NCj4gPiArI2luY2x1
ZGUgPGxpbnV4L2FjcGkuaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPg0KPiA+
ICsjaW5jbHVkZSA8bGludXgva2VybmVsLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9iaXRvcHMu
aD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L2F0b21pYy5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgv
bm90aWZpZXIuaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L3N1c3BlbmQuaD4NCj4gPiArI2luY2x1
ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPg0KPiA+ICsjaW5jbHVkZSA8YXNtL2ludGVsX3B1
bml0X2lwYy5oPg0KPiA+ICsNCj4gPiArLyogTWFpbGJveCByZWdpc3RlcnMgKi8NCj4gPiArI2Rl
ZmluZSBNQUlMQk9YX0RBVEFfTE9XCQkweDANCj4gPiArI2RlZmluZSBNQUlMQk9YX0lOVEVSRkFD
RQkJMHg0DQo+ID4gKyNkZWZpbmUJCUNNRF9SVU4JCQkoMSA8PCAzMSkNCj4gPiArI2RlZmluZQkJ
Q01EX0VSUkNPREVfTUFTSwkweEZGDQo+ID4gKyNkZWZpbmUJCUNNRF9QQVJBMV9TSElGVAkJOA0K
PiA+ICsjZGVmaW5lCQlDTURfUEFSQTJfU0hJRlQJCTE2DQo+ID4gKyNkZWZpbmUJCUNNRF9NQVNL
CQkweEZGDQo+ID4gKyNkZWZpbmUgTUFJTEJPWF9EQVRBX0hJR0gJCTB4OA0KPiA+ICsNCj4gPiAr
I2RlZmluZSBNQUlMQk9YX1JFR0lTVEVSX1NQQUNFCQkweDEwDQo+ID4gKw0KPiA+ICsjZGVmaW5l
IENNRF9USU1FT1VUX1NFQ09ORFMJCTMNCj4gPiArDQo+ID4gKy8qIFRocmVlIGV4dGVybmFsIG1h
aWxib3ggKi8NCj4gPiArZW51bSBtYWlsYm94X3R5cGUgew0KPiA+ICsJQklPU19NQUlMQk9YLA0K
PiA+ICsJR1REUklWRVJfTUFJTEJPWCwNCj4gPiArCUlTUERSSVZFUl9NQUlMQk9YLA0KPiA+ICsJ
UkVTRVJWRURfTUFJTEJPWCwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0cnVjdCBpbnRlbF9wdW5p
dF9pcGNfY29udHJvbGxlciB7DQo+ID4gKwlzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2Ow0K
PiA+ICsJc3RydWN0IG11dGV4IGxvY2s7DQo+ID4gKwl2b2lkIF9faW9tZW0gKmJhc2VbUkVTRVJW
RURfTUFJTEJPWF07DQoNCldoeSBub3QgdG8gdXNlIGV4cGxpY2l0bHkgb25lIGZpZWxkIHBlciBt
YWlsYm94Pw0KQW5kIHRoZXJlZm9yZSByZW1vdmUgZW51bS4NCg0KPiA+ICsJc3RydWN0IGNvbXBs
ZXRpb24gY21kX2NvbXBsZXRlOw0KPiA+ICsJaW50IGlycTsNCj4gPiArDQo+ID4gKwlpbnQgY21k
Ow0KPiA+ICsJZW51bSBtYWlsYm94X3R5cGUgdHlwZTsNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0
YXRpYyBjaGFyICppcGNfZXJyX3NvdXJjZXNbXSA9IHsNCj4gPiArCVtJUENfRVJSX1NVQ0NFU1Nd
ID0NCj4gPiArCQkibm8gZXJyb3IiLA0KPiA+ICsJW0lQQ19FUlJfSU5WQUxJRF9DTURdID0NCj4g
PiArCQkiaW52YWxpZCBjb21tYW5kIiwNCj4gPiArCVtJUENfRVJSX0lOVkFMSURfUEFSQU1FVEVS
XSA9DQo+ID4gKwkJImludmFsaWQgcGFyYW1ldGVyIiwNCj4gPiArCVtJUENfRVJSX0NNRF9USU1F
T1VUXSA9DQo+ID4gKwkJImNvbW1hbmQgdGltZW91dCIsDQo+ID4gKwlbSVBDX0VSUl9DTURfTE9D
S0VEXSA9DQo+ID4gKwkJImNvbW1hbmQgbG9ja2VkIiwNCj4gPiArCVtJUENfRVJSX0lOVkFMSURf
VlJfSURdID0NCj4gPiArCQkiaW52YWxpZCB2ciBpZCIsDQo+ID4gKwlbSVBDX0VSUl9WUl9FUlJd
ID0NCj4gPiArCQkidnIgZXJyb3IiLA0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RhdGljIHN0cnVj
dCBpbnRlbF9wdW5pdF9pcGNfY29udHJvbGxlciBpcGNkZXY7DQo+ID4gKw0KPiA+ICtzdGF0aWMg
aW5saW5lIHUzMiBpcGNfcmVhZF9zdGF0dXModm9pZCkNCj4gPiArew0KPiA+ICsJcmV0dXJuIHJl
YWRsKGlwY2Rldi5iYXNlW2lwY2Rldi50eXBlXSArIA0KPiA+IE1BSUxCT1hfSU5URVJGQUNFKTsN
Cj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGlubGluZSB2b2lkIGlwY193cml0ZV9jbWQodTMy
IGNtZCkNCj4gPiArew0KPiA+ICsJd3JpdGVsKGNtZCwgaXBjZGV2LmJhc2VbaXBjZGV2LnR5cGVd
ICsgTUFJTEJPWF9JTlRFUkZBQ0UpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW5saW5l
IHUzMiBpcGNfcmVhZF9kYXRhX2xvdyh2b2lkKQ0KPiA+ICt7DQo+ID4gKwlyZXR1cm4gcmVhZGwo
aXBjZGV2LmJhc2VbaXBjZGV2LnR5cGVdICsgTUFJTEJPWF9EQVRBX0xPVyk7DQo+ID4gK30NCj4g
PiArDQo+ID4gK3N0YXRpYyBpbmxpbmUgdTMyIGlwY19yZWFkX2RhdGFfaGlnaCh2b2lkKQ0KPiA+
ICt7DQo+ID4gKwlyZXR1cm4gcmVhZGwoaXBjZGV2LmJhc2VbaXBjZGV2LnR5cGVdICsgDQo+ID4g
TUFJTEJPWF9EQVRBX0hJR0gpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW5saW5lIHZv
aWQgaXBjX3dyaXRlX2RhdGFfbG93KHUzMiBkYXRhKQ0KPiA+ICt7DQo+ID4gKwl3cml0ZWwoZGF0
YSwgaXBjZGV2LmJhc2VbaXBjZGV2LnR5cGVdICsgTUFJTEJPWF9EQVRBX0xPVyk7DQo+ID4gK30N
Cj4gPiArDQo+ID4gK3N0YXRpYyBpbmxpbmUgdm9pZCBpcGNfd3JpdGVfZGF0YV9oaWdoKHUzMiBk
YXRhKQ0KPiA+ICt7DQo+ID4gKwl3cml0ZWwoZGF0YSwgaXBjZGV2LmJhc2VbaXBjZGV2LnR5cGVd
ICsgDQo+ID4gTUFJTEJPWF9EQVRBX0hJR0gpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMg
aW50IGludGVsX3B1bml0X2luaXRfY21kKHUzMiBjbWQpDQo+ID4gK3sNCj4gPiArCWNtZCAmPSBD
TURfTUFTSzsNCj4gPiArCWlmIChjbWQgPCBJUENfQklPU19QVU5JVF9DTURfQkFTRSkNCj4gPiAr
CQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArCWVsc2UgaWYgKGNtZCA8IElQQ19HVERfUFVOSVRfQ01E
X0JBU0UpDQo+ID4gKwkJaXBjZGV2LnR5cGUgPSBCSU9TX01BSUxCT1g7DQo+ID4gKwllbHNlIGlm
IChjbWQgPCBJUENfSVNQRF9QVU5JVF9DTURfQkFTRSkNCj4gPiArCQlpcGNkZXYudHlwZSA9IEdU
RFJJVkVSX01BSUxCT1g7DQo+ID4gKwllbHNlIGlmIChjbWQgPCBJUENfSVNQRF9QVU5JVF9DTURf
TUFYKQ0KPiA+ICsJCWlwY2Rldi50eXBlID0gSVNQRFJJVkVSX01BSUxCT1g7DQo+ID4gKwllbHNl
DQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQoNCkl0IHNlZW1zIHlvdSBjYWxsIHRoaXMgZWFjaCB0
aW1lIHlvdSB3YW50IHRvIHNlbmQgYSBjb21tYW5kLg0KSXQgd291bGQgYmUgbmljZXIgd2l0aG91
dCBpcGNkZXYudHlwZSBzaW5jZSB5b3UgY3JlYXRlIGFuIGFydGlmaWNpYWwNCnR5cGUgaW4gdGhl
IHNhbWUgY29kZSwgc28geW91IGFscmVhZHkga25vdyB0aGUgd2F5IGhvdyB0byBkbyB0aGF0LiBB
bmQNCnRoZXJlIGlzIG1vcmUgY29uZnVzaW9uIHNpbmNlIHlvdXIgdHlwZSBpcyBwZXJtYW5lbnRs
eSBtdXRhdGluZy4NCg0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+
ICtzdGF0aWMgaW50IGludGVsX3B1bml0X2lwY19zZW5kX2NvbW1hbmQodTMyIGNtZCkNCj4gPiAr
ew0KPiA+ICsJaW50IHJldDsNCj4gPiArDQo+ID4gKwlyZXQgPSBpbnRlbF9wdW5pdF9pbml0X2Nt
ZChjbWQpOw0KPiA+ICsJaWYgKHJldCkNCj4gPiArCQlyZXR1cm4gcmV0Ow0KPiA+ICsJaXBjZGV2
LmNtZCA9IGNtZDsNCj4gPiArCXJlaW5pdF9jb21wbGV0aW9uKCZpcGNkZXYuY21kX2NvbXBsZXRl
KTsNCj4gPiArCWlwY193cml0ZV9jbWQoY21kKTsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+
ID4gKw0KPiA+ICtzdGF0aWMgaW50IGludGVsX3B1bml0X2lwY19jaGVja19zdGF0dXModm9pZCkN
Cj4gPiArew0KPiA+ICsJaW50IGxvb3BzID0gQ01EX1RJTUVPVVRfU0VDT05EUyAqIFVTRUNfUEVS
X1NFQzsNCj4gPiArCWludCByZXQgPSAwOw0KPiA+ICsJaW50IHN0YXR1czsNCj4gPiArCWludCBl
cnJjb2RlOw0KPiA+ICsNCj4gPiArCWlmIChpcGNkZXYuaXJxKSB7DQo+ID4gKwkJaWYgKDAgPT0g
d2FpdF9mb3JfY29tcGxldGlvbl90aW1lb3V0KA0KPiA+ICsJCQkJJmlwY2Rldi5jbWRfY29tcGxl
dGUsDQoNCj4gPiArCQkJCUNNRF9USU1FT1VUX1NFQ09ORFMgKiBIWikpIHsNCg0KQ2FuIHlvdSB1
c2UgcGF0dGVybg0KaWYgKGZ1bmMoKSA9PSBjb25zdCkgaW5zdGVhZD8NCg0KPiA+ICsJCQlkZXZf
ZXJyKCZpcGNkZXYucGRldi0+ZGV2LA0KPiA+ICsJCQkJIklQQyB0aW1lZCBvdXQsIElQQ19DTUQ9
MHgleFxuIiwgDQo+ID4gaXBjZGV2LmNtZCk7DQo+ID4gKwkJCXJldHVybiAtRVRJTUVET1VUOw0K
PiA+ICsJCX0NCj4gPiArCX0gZWxzZSB7DQo+ID4gKwkJd2hpbGUgKChpcGNfcmVhZF9zdGF0dXMo
KSAmIENNRF9SVU4pICYmIC0tbG9vcHMpDQo+ID4gKwkJCXVkZWxheSgxKTsNCj4gPiArCQlpZiAo
IWxvb3BzKSB7DQo+ID4gKwkJCWRldl9lcnIoJmlwY2Rldi5wZGV2LT5kZXYsDQo+ID4gKwkJCQki
SVBDIHRpbWVkIG91dCwgSVBDX0NNRD0weCV4XG4iLCANCj4gPiBpcGNkZXYuY21kKTsNCj4gPiAr
CQkJcmV0dXJuIC1FVElNRURPVVQ7DQo+ID4gKwkJfQ0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXN0
YXR1cyA9IGlwY19yZWFkX3N0YXR1cygpOw0KPiA+ICsJZXJyY29kZSA9IHN0YXR1cyAmIENNRF9F
UlJDT0RFX01BU0s7DQo+ID4gKwlpZiAoZXJyY29kZSkgew0KPiA+ICsJCXJldCA9IC1FSU87DQo+
ID4gKwkJaWYgKGVycmNvZGUgPCBBUlJBWV9TSVpFKGlwY19lcnJfc291cmNlcykpDQo+ID4gKwkJ
CWRldl9lcnIoJmlwY2Rldi5wZGV2LT5kZXYsDQo+ID4gKwkJCQkiSVBDIGZhaWxlZDogJXMsIElQ
Q19TVFM9MHgleCwgDQo+ID4gSVBDX0NNRD0weCV4XG4iLA0KPiA+ICsJCQkJaXBjX2Vycl9zb3Vy
Y2VzW2VycmNvZGVdLCBzdGF0dXMsIA0KPiA+IGlwY2Rldi5jbWQpOw0KPiA+ICsJCWVsc2UNCj4g
PiArCQkJZGV2X2VycigmaXBjZGV2LnBkZXYtPmRldiwNCj4gPiArCQkJCSJJUEMgZmFpbGVkOiB1
bmtub3duIGVycixTVFM9MHgleCwgDQo+ID4gQ01EPTB4JXhcbiIsDQo+ID4gKwkJCQlzdGF0dXMs
IGlwY2Rldi5jbWQpOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXJldHVybiByZXQ7DQo+ID4gK30N
Cj4gPiArDQo+ID4gKy8qKg0KPiA+ICsgKiBpbnRlbF9wdW5pdF9pcGNfc2ltcGxlX2NvbW1hbmQo
KSAtIFNpbXBsZSBJUEMgY29tbWFuZA0KPiA+ICsgKiBAY21kOglJUEMgY29tbWFuZCBjb2RlLg0K
PiA+ICsgKiBAcGFyYTE6CUZpcnN0IDhiaXQgcGFyYW1ldGVyLCBzZXQgMCBpZiBpbnZhbGlkLg0K
PiA+ICsgKiBAcGFyYTI6CVNlY29uZCA4Yml0IHBhcmFtZXRlciwgc2V0IDAgaWYgaW52YWxpZC4N
Cj4gPiArICoNCj4gPiArICogU2VuZCBhIElQQyBjb21tYW5kIHRvIFB1bml0IHdoZW4gdGhlcmUg
aXMgbm8gZGF0YSB0cmFuc2FjdGlvbg0KPiA+ICsgKg0KPiA+ICsgKiBSZXR1cm46CWFuIElQQyBl
cnJvciBjb2RlIG9yIDAgb24gc3VjY2Vzcy4NCj4gPiArICovDQo+ID4gK2ludCBpbnRlbF9wdW5p
dF9pcGNfc2ltcGxlX2NvbW1hbmQoaW50IGNtZCwgaW50IHBhcmExLCBpbnQgcGFyYTIpDQo+ID4g
K3sNCj4gPiArCWludCByZXQ7DQo+ID4gKw0KPiA+ICsJbXV0ZXhfbG9jaygmaXBjZGV2LmxvY2sp
Ow0KPiA+ICsJcmV0ID0gaW50ZWxfcHVuaXRfaXBjX3NlbmRfY29tbWFuZChDTURfUlVOIHwNCj4g
PiArCQkJCQkgICBwYXJhMiA8PCANCj4gPiBDTURfUEFSQTJfU0hJRlQgfA0KPiA+ICsJCQkJCSAg
IHBhcmExIDw8IA0KPiA+IENNRF9QQVJBMV9TSElGVCB8IGNtZCk7DQo+ID4gKwlpZiAocmV0KQ0K
PiA+ICsJCWdvdG8gb3V0Ow0KPiA+ICsJcmV0ID0gaW50ZWxfcHVuaXRfaXBjX2NoZWNrX3N0YXR1
cygpOw0KPiA+ICtvdXQ6DQo+ID4gKwltdXRleF91bmxvY2soJmlwY2Rldi5sb2NrKTsNCj4gPiAr
CXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArRVhQT1JUX1NZTUJPTChpbnRlbF9wdW5pdF9pcGNf
c2ltcGxlX2NvbW1hbmQpOw0KPiA+ICsNCj4gPiArLyoqDQo+ID4gKyAqIGludGVsX3B1bml0X2lw
Y19jb21tYW5kKCkgLSBJUEMgY29tbWFuZCB3aXRoIGRhdGEgYW5kIHBvaW50ZXJzDQo+ID4gKyAq
IEBjbWQ6CUlQQyBjb21tYW5kIGNvZGUuDQo+ID4gKyAqIEBwYXJhMToJRmlyc3QgOGJpdCBwYXJh
bWV0ZXIsIHNldCAwIGlmIGludmFsaWQuDQo+ID4gKyAqIEBwYXJhMjoJU2Vjb25kIDhiaXQgcGFy
YW1ldGVyLCBzZXQgMCBpZiBpbnZhbGlkLg0KPiA+ICsgKiBAaW46CQlJbnB1dCBkYXRhLCAzMmJp
dCBmb3IgQklPUyBjbWQsIHR3byAzMmJpdCANCj4gPiBmb3IgR1REIGFuZCBJU1BELg0KPiA+ICsg
KiBAb3V0OglPdXRwdXQgZGF0YS4NCj4gPiArICoNCj4gPiArICogU2VuZCBhIElQQyBjb21tYW5k
IHRvIFB1bml0IHdpdGggZGF0YSB0cmFuc2FjdGlvbg0KPiA+ICsgKg0KPiA+ICsgKiBSZXR1cm46
CWFuIElQQyBlcnJvciBjb2RlIG9yIDAgb24gc3VjY2Vzcy4NCj4gPiArICovDQo+ID4gK2ludCBp
bnRlbF9wdW5pdF9pcGNfY29tbWFuZCh1MzIgY21kLCB1MzIgcGFyYTEsIHUzMiBwYXJhMiwgdTMy
IA0KPiA+ICppbiwgdTMyICpvdXQpDQo+ID4gK3sNCj4gPiArCWludCByZXQ7DQo+ID4gKw0KPiA+
ICsJbXV0ZXhfbG9jaygmaXBjZGV2LmxvY2spOw0KPiA+ICsJaXBjX3dyaXRlX2RhdGFfbG93KCpp
bik7DQo+ID4gKwlpZiAoaXBjZGV2LnR5cGUgPT0gR1REUklWRVJfTUFJTEJPWCB8fA0KPiA+ICsJ
CQlpcGNkZXYudHlwZSA9PSBJU1BEUklWRVJfTUFJTEJPWCkgew0KPiA+ICsJCWluKys7DQo+ID4g
KwkJaXBjX3dyaXRlX2RhdGFfaGlnaCgqaW4pOw0KDQoqKytpbg0KDQo+ID4gKwl9DQo+ID4gKwly
ZXQgPSBpbnRlbF9wdW5pdF9pcGNfc2VuZF9jb21tYW5kKENNRF9SVU4gfA0KPiA+ICsJCQkJCSAg
IHBhcmEyIDw8IA0KPiA+IENNRF9QQVJBMl9TSElGVCB8DQo+ID4gKwkJCQkJICAgcGFyYTEgPDwg
DQo+ID4gQ01EX1BBUkExX1NISUZUIHwgY21kKTsNCj4gPiArCWlmIChyZXQpDQo+ID4gKwkJZ290
byBvdXQ7DQo+ID4gKwlyZXQgPSBpbnRlbF9wdW5pdF9pcGNfY2hlY2tfc3RhdHVzKCk7DQo+ID4g
Kwkqb3V0ID0gaXBjX3JlYWRfZGF0YV9sb3coKTsNCj4gPiArCWlmIChpcGNkZXYudHlwZSA9PSBH
VERSSVZFUl9NQUlMQk9YIHx8DQo+ID4gKwkJCWlwY2Rldi50eXBlID09IElTUERSSVZFUl9NQUlM
Qk9YKSB7DQo+ID4gKwkJb3V0Kys7DQo+ID4gKwkJKm91dCA9IGlwY19yZWFkX2RhdGFfaGlnaCgp
Ow0KDQoqKytvdXQNCg0KPiA+ICsJfQ0KPiA+ICtvdXQ6DQo+ID4gKwltdXRleF91bmxvY2soJmlw
Y2Rldi5sb2NrKTsNCj4gPiArCXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArRVhQT1JUX1NZTUJP
TF9HUEwoaW50ZWxfcHVuaXRfaXBjX2NvbW1hbmQpOw0KPiA+ICsNCj4gPiArc3RhdGljIGlycXJl
dHVybl90IGludGVsX3B1bml0X2lvYyhpbnQgaXJxLCB2b2lkICpkZXZfaWQpDQo+ID4gK3sNCj4g
PiArCWNvbXBsZXRlKCZpcGNkZXYuY21kX2NvbXBsZXRlKTsNCj4gPiArCXJldHVybiBJUlFfSEFO
RExFRDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGludCBpbnRlbF9wdW5pdF9nZXRfYmFy
cyhzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgcmVz
b3VyY2UgKnJlczAsICpyZXMxOw0KPiA+ICsJdm9pZCBfX2lvbWVtICphZGRyOw0KPiA+ICsJaW50
IHNpemU7DQo+ID4gKwlpbnQgcmV0Ow0KPiA+ICsNCj4gPiArCXJlczAgPSBwbGF0Zm9ybV9nZXRf
cmVzb3VyY2UocGRldiwgSU9SRVNPVVJDRV9NRU0sIDApOw0KPiA+ICsJaWYgKCFyZXMwKSB7DQo+
ID4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAiRmFpbGVkIHRvIGdldCBpb21lbSANCj4gPiByZXNv
dXJjZVxuIik7DQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKwl9DQo+ID4gKwlzaXplID0g
cmVzb3VyY2Vfc2l6ZShyZXMwKTsNCj4gPiArCWlmICghcmVxdWVzdF9tZW1fcmVnaW9uKHJlczAt
PnN0YXJ0LCBzaXplLCBwZGV2LT5uYW1lKSkgew0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwg
IkZhaWxlZCB0byByZXF1ZXN0IGlvbWVtIA0KPiA+IHJlc291Y2VcbiIpOw0KPiA+ICsJCXJldHVy
biAtRUJVU1k7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJcmVzMSA9IHBsYXRmb3JtX2dldF9yZXNv
dXJjZShwZGV2LCBJT1JFU09VUkNFX01FTSwgMSk7DQo+ID4gKwlpZiAoIXJlczEpIHsNCj4gPiAr
CQlkZXZfZXJyKCZwZGV2LT5kZXYsICJGYWlsZWQgdG8gZ2V0IGlvbWVtIA0KPiA+IHJlc291cmNl
MVxuIik7DQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKwl9DQo+ID4gKwlzaXplID0gcmVz
b3VyY2Vfc2l6ZShyZXMxKTsNCj4gPiArCWlmICghcmVxdWVzdF9tZW1fcmVnaW9uKHJlczEtPnN0
YXJ0LCBzaXplLCBwZGV2LT5uYW1lKSkgew0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwgIkZh
aWxlZCB0byByZXF1ZXN0IGlvbWVtIA0KPiA+IHJlc291Y2UxXG4iKTsNCj4gPiArCQlyZXQgPSAt
RUJVU1k7DQo+ID4gKwkJZ290byBlcnJfcmVzMTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlhZGRy
ID0gaW9yZW1hcF9ub2NhY2hlKHJlczAtPnN0YXJ0LA0KPiA+ICsJCQkgICAgICAgcmVzb3VyY2Vf
c2l6ZShyZXMwKSArIA0KPiA+IHJlc291cmNlX3NpemUocmVzMSkpOw0KPiA+ICsJaWYgKCFhZGRy
KSB7DQo+ID4gKwkJZGV2X2VycigmcGRldi0+ZGV2LCAiRmFpbGVkIHRvIGlvcmVtYXAgaXBjIA0K
PiA+IGJhc2VcbiIpOw0KPiA+ICsJCXJldCA9IC1FTk9NRU07DQo+ID4gKwkJZ290byBlcnJfbWFw
Ow0KPiA+ICsJfQ0KPiA+ICsJaXBjZGV2LmJhc2VbQklPU19NQUlMQk9YXSA9IGFkZHI7DQo+ID4g
KwlhZGRyICs9IE1BSUxCT1hfUkVHSVNURVJfU1BBQ0U7DQo+ID4gKwlpcGNkZXYuYmFzZVtHVERS
SVZFUl9NQUlMQk9YXSA9IGFkZHI7DQo+ID4gKwlhZGRyICs9IE1BSUxCT1hfUkVHSVNURVJfU1BB
Q0U7DQo+ID4gKwlpcGNkZXYuYmFzZVtJU1BEUklWRVJfTUFJTEJPWF0gPSBhZGRyOw0KDQpXYWl0
LCBpcyB0aGlzIE1GRCBvciBqdXN0IHlvdSBsb2dpY2FsbHkgc3BsaXQgaXQuIElmIHRoZSBsYXR0
ZXIgaXMNCnRydWUsIHRoZW4gbWlnaHQgYmUgbm8gbmVlZCB0byBkaWZmZXJlbnRpYXRlIGFkZHJl
c3Nlcy4NCg0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICsNCj4gPiArZXJyX21hcDoNCj4g
PiArCXJlbGVhc2VfbWVtX3JlZ2lvbihyZXMxLT5zdGFydCwgcmVzb3VyY2Vfc2l6ZShyZXMxKSk7
DQo+ID4gK2Vycl9yZXMxOg0KPiA+ICsJcmVsZWFzZV9tZW1fcmVnaW9uKHJlczAtPnN0YXJ0LCBy
ZXNvdXJjZV9zaXplKHJlczApKTsNCj4gPiArCXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArDQo+
ID4gK3N0YXRpYyBpbnQgaW50ZWxfcHVuaXRfaXBjX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZp
Y2UgKnBkZXYpDQo+ID4gK3sNCj4gPiArCWludCBpcnE7DQo+ID4gKwlpbnQgcmV0Ow0KPiA+ICsN
Cj4gPiArCWlycSA9IHBsYXRmb3JtX2dldF9pcnEocGRldiwgMCk7DQo+ID4gKwlpZiAoaXJxIDwg
MCkgew0KPiA+ICsJCWlwY2Rldi5pcnEgPSAwOw0KPiA+ICsJCWRldl93YXJuKCZwZGV2LT5kZXYs
ICJDb3VsZCBub3QgZ2V0IGlycSANCj4gPiBudW1iZXJcbiIpOw0KPiA+ICsJfSBlbHNlIHsNCj4g
PiArCQlpZiAocmVxdWVzdF9pcnEoaXJxLCBpbnRlbF9wdW5pdF9pb2MsIA0KPiA+IElSUUZfTk9f
U1VTUEVORCwNCj4gPiArCQkJCSJpbnRlbF9wdW5pdF9pcGMiLCAmaXBjZGV2KSkgew0KPiA+ICsJ
CQlkZXZfZXJyKCZwZGV2LT5kZXYsICJyZXF1ZXN0IGlycSAlZFxuIiwgDQo+ID4gaXJxKTsNCj4g
PiArCQkJcmV0dXJuIC1FQlVTWTsNCj4gPiArCQl9DQo+ID4gKwkJaXBjZGV2LmlycSA9IGlycTsN
Cj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXQgPSBpbnRlbF9wdW5pdF9nZXRfYmFycyhwZGV2KTsN
Cj4gPiArCWlmIChyZXQpIHsNCj4gPiArCQlpZiAoaXBjZGV2LmlycSkNCj4gPiArCQkJZnJlZV9p
cnEoaXBjZGV2LmlycSwgJmlwY2Rldik7DQo+ID4gKwkJcmV0dXJuIHJldDsNCj4gPiArCX0NCj4g
PiArDQo+ID4gKwlpcGNkZXYucGRldiA9IHBkZXY7DQo+ID4gKwltdXRleF9pbml0KCZpcGNkZXYu
bG9jayk7DQo+ID4gKwlpbml0X2NvbXBsZXRpb24oJmlwY2Rldi5jbWRfY29tcGxldGUpOw0KPiA+
ICsJcmV0dXJuIHJldDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGludCBpbnRlbF9wdW5p
dF9pcGNfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpDQo+ID4gK3sNCj4gPiAr
CXN0cnVjdCByZXNvdXJjZSAqcmVzOw0KPiA+ICsNCj4gPiArCWlmIChpcGNkZXYuaXJxKQ0KPiA+
ICsJCWZyZWVfaXJxKGlwY2Rldi5pcnEsICZpcGNkZXYpOw0KPiA+ICsJaW91bm1hcChpcGNkZXYu
YmFzZVtCSU9TX01BSUxCT1hdKTsNCj4gPiArCXJlcyA9IHBsYXRmb3JtX2dldF9yZXNvdXJjZShw
ZGV2LCBJT1JFU09VUkNFX01FTSwgMCk7DQo+ID4gKwlpZiAocmVzKQ0KPiA+ICsJCXJlbGVhc2Vf
bWVtX3JlZ2lvbihyZXMtPnN0YXJ0LCANCj4gPiByZXNvdXJjZV9zaXplKHJlcykpOw0KPiA+ICsJ
cmVzID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNlKHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAxKTsNCj4g
PiArCWlmIChyZXMpDQo+ID4gKwkJcmVsZWFzZV9tZW1fcmVnaW9uKHJlcy0+c3RhcnQsIA0KPiA+
IHJlc291cmNlX3NpemUocmVzKSk7DQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4g
PiArI2lmZGVmIENPTkZJR19BQ1BJDQoNCk5vIG5lZWQgdG8gaGF2ZSAjaWZkZWYgaGVyZS4NCg0K
PiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGFjcGlfZGV2aWNlX2lkIHB1bml0X2lwY19hY3BpX2lk
c1tdID0gew0KPiA+ICsJeyJJTlQzNEQ0IiwgMH0NCj4gPiArfTsNCj4gPiArI2VuZGlmDQo+ID4g
Kw0KPiA+ICtzdGF0aWMgc3RydWN0IHBsYXRmb3JtX2RyaXZlciBpbnRlbF9wdW5pdF9pcGNfZHJp
dmVyID0gew0KPiA+ICsJLnByb2JlID0gaW50ZWxfcHVuaXRfaXBjX3Byb2JlLA0KPiA+ICsJLnJl
bW92ZSA9IGludGVsX3B1bml0X2lwY19yZW1vdmUsDQo+ID4gKwkuZHJpdmVyID0gew0KPiA+ICsJ
CS5uYW1lID0gImludGVsX3B1bml0X2lwYyIsDQo+ID4gKwkJLmFjcGlfbWF0Y2hfdGFibGUgPSBB
Q1BJX1BUUihwdW5pdF9pcGNfYWNwaV9pZHMpLA0KPiA+ICsJfSwNCj4gPiArfTsNCj4gPiArDQo+
ID4gK3N0YXRpYyBpbnQgX19pbml0IGludGVsX3B1bml0X2lwY19pbml0KHZvaWQpDQo+ID4gK3sN
Cj4gPiArCXJldHVybiBwbGF0Zm9ybV9kcml2ZXJfcmVnaXN0ZXIoJmludGVsX3B1bml0X2lwY19k
cml2ZXIpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCBfX2V4aXQgaW50ZWxfcHVu
aXRfaXBjX2V4aXQodm9pZCkNCj4gPiArew0KPiA+ICsJcGxhdGZvcm1fZHJpdmVyX3VucmVnaXN0
ZXIoJmludGVsX3B1bml0X2lwY19kcml2ZXIpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtNT0RVTEVf
QVVUSE9SKCJaaGEgUWlwZW5nIDxxaXBlbmcuemhhQGludGVsLmNvbT4iKTsNCj4gPiArTU9EVUxF
X0RFU0NSSVBUSU9OKCJJbnRlbCBQdW5pdCBJUEMgZHJpdmVyIik7DQo+ID4gK01PRFVMRV9MSUNF
TlNFKCJHUEwiKTsNCj4gPiArDQo+ID4gKy8qIFNvbWUgbW9kdWxlcyBhcmUgZGVwZW5kZW50IG9u
IHRoaXMsIHNvIGluaXQgZWFybGllciAqLw0KDQpTbywgaW5pdGNhbGxzIG1ha2Ugc2Vuc2Ugb25s
eSBmb3IgYnVpbHQtaW4gZHJpdmVycy4gV2hpY2ggb25lIHdpbGwNCnJlcXVpcmUgdGhhdD8NCg0K
PiA+ICtmc19pbml0Y2FsbChpbnRlbF9wdW5pdF9pcGNfaW5pdCk7DQo+ID4gK21vZHVsZV9leGl0
KGludGVsX3B1bml0X2lwY19leGl0KTsNCj4gPiAtLSANCj4gPiAxLjguMy4yDQo+ID4gDQo+ID4g
DQo+IA0KDQotLSANCkFuZHkgU2hldmNoZW5rbyA8YW5kcml5LnNoZXZjaGVua29AaW50ZWwuY29t
Pg0KSW50ZWwgRmlubGFuZCBPeQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KSW50ZWwgRmlubGFuZCBPeQpSZWdpc3Rl
cmVkIEFkZHJlc3M6IFBMIDI4MSwgMDAxODEgSGVsc2lua2kgCkJ1c2luZXNzIElkZW50aXR5IENv
ZGU6IDAzNTc2MDYgLSA0IApEb21pY2lsZWQgaW4gSGVsc2lua2kgCgpUaGlzIGUtbWFpbCBhbmQg
YW55IGF0dGFjaG1lbnRzIG1heSBjb250YWluIGNvbmZpZGVudGlhbCBtYXRlcmlhbCBmb3IKdGhl
IHNvbGUgdXNlIG9mIHRoZSBpbnRlbmRlZCByZWNpcGllbnQocykuIEFueSByZXZpZXcgb3IgZGlz
dHJpYnV0aW9uCmJ5IG90aGVycyBpcyBzdHJpY3RseSBwcm9oaWJpdGVkLiBJZiB5b3UgYXJlIG5v
dCB0aGUgaW50ZW5kZWQKcmVjaXBpZW50LCBwbGVhc2UgY29udGFjdCB0aGUgc2VuZGVyIGFuZCBk
ZWxldGUgYWxsIGNvcGllcy4K

--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matt Fleming Aug. 18, 2015, 3:50 p.m. UTC | #4
On Mon, 2015-08-10 at 16:04 +0100, Shevchenko, Andriy wrote:
> 
> Matt, by the way intel_pmc_ipc.c module creates iTCO device (LPC bus).
> Does it look correct?

Yeah, it looks OK to me. Note there are changes queued up in Lee Jones'
tree for changing the lpc_ich_info into itco_wdt_platform_data.

Btw, is there a reason that intel_pmc_ipc doesn't use the mfd_* API
since it's concerned with multiple functions?

--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Darren Hart Aug. 25, 2015, 8:33 p.m. UTC | #5
On Tue, Aug 18, 2015 at 04:50:56PM +0100, Matt Fleming wrote:
> On Mon, 2015-08-10 at 16:04 +0100, Shevchenko, Andriy wrote:
> > 
> > Matt, by the way intel_pmc_ipc.c module creates iTCO device (LPC bus).
> > Does it look correct?
> 
> Yeah, it looks OK to me. Note there are changes queued up in Lee Jones'
> tree for changing the lpc_ich_info into itco_wdt_platform_data.
> 
> Btw, is there a reason that intel_pmc_ipc doesn't use the mfd_* API
> since it's concerned with multiple functions?

Qipeng?

I saw the v2 come in, but didn't see this question addressed in the changelog.
qipeng.zha Aug. 26, 2015, 8:22 a.m. UTC | #6
On Tue, Aug 18, 2015 at 04:50:56PM +0100, Matt Fleming wrote:
> On Mon, 2015-08-10 at 16:04 +0100, Shevchenko, Andriy wrote:
> > 
> > Matt, by the way intel_pmc_ipc.c module creates iTCO device (LPC bus).
> > Does it look correct?
> 
> Yeah, it looks OK to me. Note there are changes queued up in Lee Jones'
> tree for changing the lpc_ich_info into itco_wdt_platform_data.
> 
> Btw, is there a reason that intel_pmc_ipc doesn't use the mfd_* API 
> since it's concerned with multiple functions?

> Qipeng?

> I saw the v2 come in, but didn't see this question addressed in the changelog.

When design P-unit driver, we got feedback from BIOS that there will allocate a dedicate
ACPI id for P-unit controller, and finally BIOS decide not do that, just put some necessary 
Resource in PMC acpi table.
After that, we got request to do same thing for iTco.

I agree it's better to update pmc driver to use mfd_add_devices to create punit and iTco device.

--
Darren Hart
Intel Open Source Technology Center
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in the body of a message to majordomo@vger.kernel.org More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Darren Hart Aug. 28, 2015, 6:57 p.m. UTC | #7
On Wed, Aug 26, 2015 at 08:22:33AM +0000, Zha, Qipeng wrote:
> On Tue, Aug 18, 2015 at 04:50:56PM +0100, Matt Fleming wrote:
> > On Mon, 2015-08-10 at 16:04 +0100, Shevchenko, Andriy wrote:
> > > 
> > > Matt, by the way intel_pmc_ipc.c module creates iTCO device (LPC bus).
> > > Does it look correct?
> > 
> > Yeah, it looks OK to me. Note there are changes queued up in Lee Jones'
> > tree for changing the lpc_ich_info into itco_wdt_platform_data.
> > 
> > Btw, is there a reason that intel_pmc_ipc doesn't use the mfd_* API 
> > since it's concerned with multiple functions?
> 
> > Qipeng?
> 
> > I saw the v2 come in, but didn't see this question addressed in the changelog.
> 
> When design P-unit driver, we got feedback from BIOS that there will allocate a dedicate
> ACPI id for P-unit controller, and finally BIOS decide not do that, just put some necessary 
> Resource in PMC acpi table.
> After that, we got request to do same thing for iTco.
> 
> I agree it's better to update pmc driver to use mfd_add_devices to create punit and iTco device.

Does this mean there sill be a v4 I should be waiting for?
diff mbox

Patch

diff --git a/arch/x86/include/asm/intel_punit_ipc.h b/arch/x86/include/asm/intel_punit_ipc.h
new file mode 100644
index 0000000..08e3e14
--- /dev/null
+++ b/arch/x86/include/asm/intel_punit_ipc.h
@@ -0,0 +1,101 @@ 
+#ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
+#define  _ASM_X86_INTEL_PUNIT_IPC_H_
+
+/* Commands supported on GLM core, are handled by different bars,
+ * unify these commands together here
+ */
+#define IPC_BIOS_PUNIT_CMD_BASE				0x00
+#define IPC_GTD_PUNIT_CMD_BASE				0x20
+#define IPC_ISPD_PUNIT_CMD_BASE				0x40
+
+/* BIOS => Pcode commands */
+#define IPC_BIOS_PUNIT_CMD_ZERO			(IPC_BIOS_PUNIT_CMD_BASE + 0x00)
+#define IPC_BIOS_PUNIT_CMD_VR_INTERFACE		(IPC_BIOS_PUNIT_CMD_BASE + 0x01)
+#define IPC_BIOS_PUNIT_CMD_READ_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x02)
+#define IPC_BIOS_PUNIT_CMD_WRITE_PCS		(IPC_BIOS_PUNIT_CMD_BASE + 0x03)
+#define IPC_BIOS_PUNIT_CMD_READ_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x04)
+#define IPC_BIOS_PUNIT_CMD_WRITE_PCU_CONFIG	(IPC_BIOS_PUNIT_CMD_BASE + 0x05)
+#define IPC_BIOS_PUNIT_CMD_READ_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x06)
+#define IPC_BIOS_PUNIT_CMD_WRITE_PL1_SETTING	(IPC_BIOS_PUNIT_CMD_BASE + 0x07)
+#define IPC_BIOS_PUNIT_CMD_TRIGGER_VDD_RAM	(IPC_BIOS_PUNIT_CMD_BASE + 0x08)
+#define IPC_BIOS_PUNIT_CMD_READ_TELE_INFO	(IPC_BIOS_PUNIT_CMD_BASE + 0x09)
+#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0a)
+#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE_CTRL \
+						(IPC_BIOS_PUNIT_CMD_BASE + 0x0b)
+#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x0c)
+#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT_CTRL \
+						(IPC_BIOS_PUNIT_CMD_BASE + 0x0d)
+#define IPC_BIOS_PUNIT_CMD_READ_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0e)
+#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_TRACE	(IPC_BIOS_PUNIT_CMD_BASE + 0x0f)
+#define IPC_BIOS_PUNIT_CMD_READ_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x10)
+#define IPC_BIOS_PUNIT_CMD_WRITE_TELE_EVENT	(IPC_BIOS_PUNIT_CMD_BASE + 0x11)
+#define IPC_BIOS_PUNIT_CMD_READ_MODULE_TEMP	(IPC_BIOS_PUNIT_CMD_BASE + 0x12)
+#define IPC_BIOS_PUNIT_CMD_RESERVED		(IPC_BIOS_PUNIT_CMD_BASE + 0x13)
+#define IPC_BIOS_PUNIT_CMD_READ_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x14)
+#define IPC_BIOS_PUNIT_CMD_WRITE_VOLTAGE_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x15)
+#define IPC_BIOS_PUNIT_CMD_READ_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x16)
+#define IPC_BIOS_PUNIT_CMD_WRITE_RATIO_OVER	(IPC_BIOS_PUNIT_CMD_BASE + 0x17)
+#define IPC_BIOS_PUNIT_CMD_READ_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x18)
+#define IPC_BIOS_PUNIT_CMD_WRITE_VF_GL_CTRL	(IPC_BIOS_PUNIT_CMD_BASE + 0x19)
+#define IPC_BIOS_PUNIT_CMD_READ_FM_SOC_TEMP_THRESH \
+						(IPC_BIOS_PUNIT_CMD_BASE + 0x1a)
+#define IPC_BIOS_PUNIT_CMD_WRITE_FM_SOC_TEMP_THRESH \
+						(IPC_BIOS_PUNIT_CMD_BASE + 0x1b)
+
+/*GT Driver => Pcode commands*/
+#define IPC_GTD_PUNIT_CMD_ZERO			(IPC_GTD_PUNIT_CMD_BASE + 0x00)
+#define IPC_GTD_PUNIT_CMD_CONFIG		(IPC_GTD_PUNIT_CMD_BASE + 0x01)
+#define IPC_GTD_PUNIT_CMD_READ_ICCP_LIC_CDYN_SCAL \
+						(IPC_GTD_PUNIT_CMD_BASE + 0x02)
+#define IPC_GTD_PUNIT_CMD_WRITE_ICCP_LIC_CDYN_SCAL \
+						(IPC_GTD_PUNIT_CMD_BASE + 0x03)
+#define IPC_GTD_PUNIT_CMD_GET_WM_VAL		(IPC_GTD_PUNIT_CMD_BASE + 0x06)
+#define IPC_GTD_PUNIT_CMD_WRITE_CONFIG_WISHREQ	(IPC_GTD_PUNIT_CMD_BASE + 0x07)
+#define IPC_GTD_PUNIT_CMD_READ_REQ_DUTY_CYCLE	(IPC_GTD_PUNIT_CMD_BASE + 0x16)
+#define IPC_GTD_PUNIT_CMD_DIS_VOL_FREQ_CHANGE_REQUEST \
+						(IPC_GTD_PUNIT_CMD_BASE + 0x17)
+#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_CTRL	(IPC_GTD_PUNIT_CMD_BASE + 0x1a)
+#define IPC_GTD_PUNIT_CMD_DYNA_DUTY_CYCLE_TUNING \
+						(IPC_GTD_PUNIT_CMD_BASE + 0x1c)
+
+/* ISP Driver => Pcode commands */
+#define IPC_ISPD_PUNIT_CMD_ZERO			(IPC_ISPD_PUNIT_CMD_BASE + 0x00)
+#define IPC_ISPD_PUNIT_CMD_CONFIG		(IPC_ISPD_PUNIT_CMD_BASE + 0x01)
+#define IPC_ISPD_PUNIT_CMD_GET_ISP_LTR_VAL	(IPC_ISPD_PUNIT_CMD_BASE + 0x02)
+#define IPC_ISPD_PUNIT_CMD_ACCESS_IU_FREQ_BOUNDS \
+						(IPC_ISPD_PUNIT_CMD_BASE + 0x03)
+#define IPC_ISPD_PUNIT_CMD_READ_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x04)
+#define IPC_ISPD_PUNIT_CMD_WRITE_CDYN_LEVEL	(IPC_ISPD_PUNIT_CMD_BASE + 0x05)
+#define IPC_ISPD_PUNIT_CMD_MAX			(IPC_ISPD_PUNIT_CMD_BASE + 0x06)
+
+/* Error codes */
+#define IPC_ERR_SUCCESS				0
+#define IPC_ERR_INVALID_CMD			1
+#define IPC_ERR_INVALID_PARAMETER		2
+#define IPC_ERR_CMD_TIMEOUT			3
+#define IPC_ERR_CMD_LOCKED			4
+#define IPC_ERR_INVALID_VR_ID			5
+#define IPC_ERR_VR_ERR				6
+
+#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
+
+int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
+int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
+
+#else
+
+static intline int intel_punit_ipc_simple_command(int cmd,
+						  int para1, int para2);
+{
+	return -EINVAL;
+}
+
+static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
+					  u32 *in, u32 *out);
+{
+	return -EINVAL;
+}
+
+#endif /*CONFIG_INTEL_PUNIT_IPC*/
+
+#endif
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 346f1fd..42ada9b 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -891,4 +891,10 @@  config INTEL_PMC_IPC
 	The PMC is an ARC processor which defines IPC commands for communication
 	with other entities in the CPU.
 
+config INTEL_PUNIT_IPC
+	tristate "Intel PUNIT IPC Driver"
+	default y
+	---help---
+	  IPC is used to bridge the communications between kernel and PUNIT
+
 endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 1051372..eea765f 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -59,3 +59,4 @@  obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
 obj-$(CONFIG_PVPANIC)           += pvpanic.o
 obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
 obj-$(CONFIG_INTEL_PMC_IPC)	+= intel_pmc_ipc.o
+obj-$(CONFIG_INTEL_PUNIT_IPC)	+= intel_punit_ipc.o
diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
new file mode 100644
index 0000000..78cb794
--- /dev/null
+++ b/drivers/platform/x86/intel_punit_ipc.c
@@ -0,0 +1,388 @@ 
+/*
+ * intel_punit_ipc.c: Driver for the Intel Punit Mailbox IPC mechanism
+ *
+ * (C) Copyright 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The heart of the Punit is the Foxton microcontroller and its firmware,
+ * which provide mailbox interface for power management usage.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/atomic.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
+#include <linux/platform_device.h>
+#include <asm/intel_punit_ipc.h>
+
+/* Mailbox registers */
+#define MAILBOX_DATA_LOW		0x0
+#define MAILBOX_INTERFACE		0x4
+#define		CMD_RUN			(1 << 31)
+#define		CMD_ERRCODE_MASK	0xFF
+#define		CMD_PARA1_SHIFT		8
+#define		CMD_PARA2_SHIFT		16
+#define		CMD_MASK		0xFF
+#define MAILBOX_DATA_HIGH		0x8
+
+#define MAILBOX_REGISTER_SPACE		0x10
+
+#define CMD_TIMEOUT_SECONDS		3
+
+/* Three external mailbox */
+enum mailbox_type {
+	BIOS_MAILBOX,
+	GTDRIVER_MAILBOX,
+	ISPDRIVER_MAILBOX,
+	RESERVED_MAILBOX,
+};
+
+struct intel_punit_ipc_controller {
+	struct platform_device *pdev;
+	struct mutex lock;
+	void __iomem *base[RESERVED_MAILBOX];
+	struct completion cmd_complete;
+	int irq;
+
+	int cmd;
+	enum mailbox_type type;
+};
+
+static char *ipc_err_sources[] = {
+	[IPC_ERR_SUCCESS] =
+		"no error",
+	[IPC_ERR_INVALID_CMD] =
+		"invalid command",
+	[IPC_ERR_INVALID_PARAMETER] =
+		"invalid parameter",
+	[IPC_ERR_CMD_TIMEOUT] =
+		"command timeout",
+	[IPC_ERR_CMD_LOCKED] =
+		"command locked",
+	[IPC_ERR_INVALID_VR_ID] =
+		"invalid vr id",
+	[IPC_ERR_VR_ERR] =
+		"vr error",
+};
+
+static struct intel_punit_ipc_controller ipcdev;
+
+static inline u32 ipc_read_status(void)
+{
+	return readl(ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
+}
+
+static inline void ipc_write_cmd(u32 cmd)
+{
+	writel(cmd, ipcdev.base[ipcdev.type] + MAILBOX_INTERFACE);
+}
+
+static inline u32 ipc_read_data_low(void)
+{
+	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
+}
+
+static inline u32 ipc_read_data_high(void)
+{
+	return readl(ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
+}
+
+static inline void ipc_write_data_low(u32 data)
+{
+	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_LOW);
+}
+
+static inline void ipc_write_data_high(u32 data)
+{
+	writel(data, ipcdev.base[ipcdev.type] + MAILBOX_DATA_HIGH);
+}
+
+static int intel_punit_init_cmd(u32 cmd)
+{
+	cmd &= CMD_MASK;
+	if (cmd < IPC_BIOS_PUNIT_CMD_BASE)
+		return -EINVAL;
+	else if (cmd < IPC_GTD_PUNIT_CMD_BASE)
+		ipcdev.type = BIOS_MAILBOX;
+	else if (cmd < IPC_ISPD_PUNIT_CMD_BASE)
+		ipcdev.type = GTDRIVER_MAILBOX;
+	else if (cmd < IPC_ISPD_PUNIT_CMD_MAX)
+		ipcdev.type = ISPDRIVER_MAILBOX;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int intel_punit_ipc_send_command(u32 cmd)
+{
+	int ret;
+
+	ret = intel_punit_init_cmd(cmd);
+	if (ret)
+		return ret;
+	ipcdev.cmd = cmd;
+	reinit_completion(&ipcdev.cmd_complete);
+	ipc_write_cmd(cmd);
+	return 0;
+}
+
+static int intel_punit_ipc_check_status(void)
+{
+	int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
+	int ret = 0;
+	int status;
+	int errcode;
+
+	if (ipcdev.irq) {
+		if (0 == wait_for_completion_timeout(
+				&ipcdev.cmd_complete,
+				CMD_TIMEOUT_SECONDS * HZ)) {
+			dev_err(&ipcdev.pdev->dev,
+				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
+			return -ETIMEDOUT;
+		}
+	} else {
+		while ((ipc_read_status() & CMD_RUN) && --loops)
+			udelay(1);
+		if (!loops) {
+			dev_err(&ipcdev.pdev->dev,
+				"IPC timed out, IPC_CMD=0x%x\n", ipcdev.cmd);
+			return -ETIMEDOUT;
+		}
+	}
+
+	status = ipc_read_status();
+	errcode = status & CMD_ERRCODE_MASK;
+	if (errcode) {
+		ret = -EIO;
+		if (errcode < ARRAY_SIZE(ipc_err_sources))
+			dev_err(&ipcdev.pdev->dev,
+				"IPC failed: %s, IPC_STS=0x%x, IPC_CMD=0x%x\n",
+				ipc_err_sources[errcode], status, ipcdev.cmd);
+		else
+			dev_err(&ipcdev.pdev->dev,
+				"IPC failed: unknown err,STS=0x%x, CMD=0x%x\n",
+				status, ipcdev.cmd);
+	}
+
+	return ret;
+}
+
+/**
+ * intel_punit_ipc_simple_command() - Simple IPC command
+ * @cmd:	IPC command code.
+ * @para1:	First 8bit parameter, set 0 if invalid.
+ * @para2:	Second 8bit parameter, set 0 if invalid.
+ *
+ * Send a IPC command to Punit when there is no data transaction
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
+{
+	int ret;
+
+	mutex_lock(&ipcdev.lock);
+	ret = intel_punit_ipc_send_command(CMD_RUN |
+					   para2 << CMD_PARA2_SHIFT |
+					   para1 << CMD_PARA1_SHIFT | cmd);
+	if (ret)
+		goto out;
+	ret = intel_punit_ipc_check_status();
+out:
+	mutex_unlock(&ipcdev.lock);
+	return ret;
+}
+EXPORT_SYMBOL(intel_punit_ipc_simple_command);
+
+/**
+ * intel_punit_ipc_command() - IPC command with data and pointers
+ * @cmd:	IPC command code.
+ * @para1:	First 8bit parameter, set 0 if invalid.
+ * @para2:	Second 8bit parameter, set 0 if invalid.
+ * @in:		Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
+ * @out:	Output data.
+ *
+ * Send a IPC command to Punit with data transaction
+ *
+ * Return:	an IPC error code or 0 on success.
+ */
+int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
+{
+	int ret;
+
+	mutex_lock(&ipcdev.lock);
+	ipc_write_data_low(*in);
+	if (ipcdev.type == GTDRIVER_MAILBOX ||
+			ipcdev.type == ISPDRIVER_MAILBOX) {
+		in++;
+		ipc_write_data_high(*in);
+	}
+	ret = intel_punit_ipc_send_command(CMD_RUN |
+					   para2 << CMD_PARA2_SHIFT |
+					   para1 << CMD_PARA1_SHIFT | cmd);
+	if (ret)
+		goto out;
+	ret = intel_punit_ipc_check_status();
+	*out = ipc_read_data_low();
+	if (ipcdev.type == GTDRIVER_MAILBOX ||
+			ipcdev.type == ISPDRIVER_MAILBOX) {
+		out++;
+		*out = ipc_read_data_high();
+	}
+out:
+	mutex_unlock(&ipcdev.lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
+
+static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
+{
+	complete(&ipcdev.cmd_complete);
+	return IRQ_HANDLED;
+}
+
+static int intel_punit_get_bars(struct platform_device *pdev)
+{
+	struct resource *res0, *res1;
+	void __iomem *addr;
+	int size;
+	int ret;
+
+	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res0) {
+		dev_err(&pdev->dev, "Failed to get iomem resource\n");
+		return -EINVAL;
+	}
+	size = resource_size(res0);
+	if (!request_mem_region(res0->start, size, pdev->name)) {
+		dev_err(&pdev->dev, "Failed to request iomem resouce\n");
+		return -EBUSY;
+	}
+
+	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res1) {
+		dev_err(&pdev->dev, "Failed to get iomem resource1\n");
+		return -EINVAL;
+	}
+	size = resource_size(res1);
+	if (!request_mem_region(res1->start, size, pdev->name)) {
+		dev_err(&pdev->dev, "Failed to request iomem resouce1\n");
+		ret = -EBUSY;
+		goto err_res1;
+	}
+
+	addr = ioremap_nocache(res0->start,
+			       resource_size(res0) + resource_size(res1));
+	if (!addr) {
+		dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
+		ret = -ENOMEM;
+		goto err_map;
+	}
+	ipcdev.base[BIOS_MAILBOX] = addr;
+	addr += MAILBOX_REGISTER_SPACE;
+	ipcdev.base[GTDRIVER_MAILBOX] = addr;
+	addr += MAILBOX_REGISTER_SPACE;
+	ipcdev.base[ISPDRIVER_MAILBOX] = addr;
+
+	return 0;
+
+err_map:
+	release_mem_region(res1->start, resource_size(res1));
+err_res1:
+	release_mem_region(res0->start, resource_size(res0));
+	return ret;
+}
+
+static int intel_punit_ipc_probe(struct platform_device *pdev)
+{
+	int irq;
+	int ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ipcdev.irq = 0;
+		dev_warn(&pdev->dev, "Could not get irq number\n");
+	} else {
+		if (request_irq(irq, intel_punit_ioc, IRQF_NO_SUSPEND,
+				"intel_punit_ipc", &ipcdev)) {
+			dev_err(&pdev->dev, "request irq %d\n", irq);
+			return -EBUSY;
+		}
+		ipcdev.irq = irq;
+	}
+
+	ret = intel_punit_get_bars(pdev);
+	if (ret) {
+		if (ipcdev.irq)
+			free_irq(ipcdev.irq, &ipcdev);
+		return ret;
+	}
+
+	ipcdev.pdev = pdev;
+	mutex_init(&ipcdev.lock);
+	init_completion(&ipcdev.cmd_complete);
+	return ret;
+}
+
+static int intel_punit_ipc_remove(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	if (ipcdev.irq)
+		free_irq(ipcdev.irq, &ipcdev);
+	iounmap(ipcdev.base[BIOS_MAILBOX]);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+	return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id punit_ipc_acpi_ids[] = {
+	{"INT34D4", 0}
+};
+#endif
+
+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),
+	},
+};
+
+static int __init intel_punit_ipc_init(void)
+{
+	return platform_driver_register(&intel_punit_ipc_driver);
+}
+
+static void __exit intel_punit_ipc_exit(void)
+{
+	platform_driver_unregister(&intel_punit_ipc_driver);
+}
+
+MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
+MODULE_DESCRIPTION("Intel Punit IPC driver");
+MODULE_LICENSE("GPL");
+
+/* Some modules are dependent on this, so init earlier */
+fs_initcall(intel_punit_ipc_init);
+module_exit(intel_punit_ipc_exit);