From: Daniel Drubin <daniel.dru...@intel.com>

This layer is responsible for
- Enumerating over PCI bus
- Inform FW about host readiness
- Provide HW interface to transport layer for control and messages
- Interrupt handling and routing

Signed-off-by: Srinivas Pandruvada <srinivas.pandruv...@linux.intel.com>
---
 drivers/hid/intel-ish-hid/Kconfig           |   5 +
 drivers/hid/intel-ish-hid/Makefile          |   6 +
 drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h | 220 +++++++++
 drivers/hid/intel-ish-hid/ipc/hw-ish.h      |  71 +++
 drivers/hid/intel-ish-hid/ipc/ipc.c         | 710 ++++++++++++++++++++++++++++
 drivers/hid/intel-ish-hid/ipc/pci-ish.c     | 238 ++++++++++
 drivers/hid/intel-ish-hid/ipc/utils.h       |  65 +++
 include/trace/events/intel_ish.h            |  30 ++
 8 files changed, 1345 insertions(+)
 create mode 100644 drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
 create mode 100644 drivers/hid/intel-ish-hid/ipc/hw-ish.h
 create mode 100644 drivers/hid/intel-ish-hid/ipc/ipc.c
 create mode 100644 drivers/hid/intel-ish-hid/ipc/pci-ish.c
 create mode 100644 drivers/hid/intel-ish-hid/ipc/utils.h
 create mode 100644 include/trace/events/intel_ish.h

diff --git a/drivers/hid/intel-ish-hid/Kconfig 
b/drivers/hid/intel-ish-hid/Kconfig
index 8914f3b..88967a1 100644
--- a/drivers/hid/intel-ish-hid/Kconfig
+++ b/drivers/hid/intel-ish-hid/Kconfig
@@ -5,10 +5,15 @@ config INTEL_ISH_HID_TRANSPORT
        bool
        default n
 
+config INTEL_ISH_HID_IPC
+       bool
+       default n
+
 config INTEL_ISH_HID
        bool "Intel Integrated Sensor Hub"
        default n
        select INTEL_ISH_HID_TRANSPORT
+       select INTEL_ISH_HID_IPC
        help
          The Integrated Sensor Hub (ISH) enables the ability to offload
          sensor polling and algorithm processing to a dedicated low power
diff --git a/drivers/hid/intel-ish-hid/Makefile 
b/drivers/hid/intel-ish-hid/Makefile
index a5eaa6e..2c83cb9 100644
--- a/drivers/hid/intel-ish-hid/Makefile
+++ b/drivers/hid/intel-ish-hid/Makefile
@@ -8,3 +8,9 @@ intel-ishtp-objs += ishtp/hbm.o
 intel-ishtp-objs += ishtp/client.o
 intel-ishtp-objs += ishtp/bus.o
 intel-ishtp-objs += ishtp/dma-if.o
+
+obj-$(CONFIG_INTEL_ISH_HID_IPC) += intel-ish-ipc.o
+intel-ish-ipc-objs := ipc/ipc.o
+intel-ish-ipc-objs += ipc/pci-ish.o
+
+ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h 
b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
new file mode 100644
index 0000000..f6eac6d
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish-regs.h
@@ -0,0 +1,220 @@
+/*
+ * ISH registers definitions
+ *
+ * Copyright (c) 2012-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _ISHTP_ISH_REGS_H_
+#define _ISHTP_ISH_REGS_H_
+
+
+/*** IPC PCI Offsets and sizes ***/
+/* ISH IPC Base Address */
+#define IPC_REG_BASE           0x0000
+/* Peripheral Interrupt Status Register */
+#define IPC_REG_PISR_CHV_AB      (IPC_REG_BASE + 0x00)
+/* Peripheral Interrupt Mask Register */
+#define IPC_REG_PIMR_CHV_AB      (IPC_REG_BASE + 0x04)
+/*BXT, CHV_K0*/
+/*Peripheral Interrupt Status Register */
+#define IPC_REG_PISR_BXT        (IPC_REG_BASE + 0x0C)
+/*Peripheral Interrupt Mask Register */
+#define IPC_REG_PIMR_BXT        (IPC_REG_BASE + 0x08)
+/***********************************/
+/* ISH Host Firmware status Register */
+#define IPC_REG_ISH_HOST_FWSTS (IPC_REG_BASE + 0x34)
+/* Host Communication Register */
+#define IPC_REG_HOST_COMM      (IPC_REG_BASE + 0x38)
+/* Reset register */
+#define IPC_REG_ISH_RST                (IPC_REG_BASE + 0x44)
+
+/* Inbound doorbell register Host to ISH */
+#define IPC_REG_HOST2ISH_DRBL  (IPC_REG_BASE + 0x48)
+/* Outbound doorbell register ISH to Host */
+#define IPC_REG_ISH2HOST_DRBL  (IPC_REG_BASE + 0x54)
+/* ISH to HOST message registers */
+#define IPC_REG_ISH2HOST_MSG   (IPC_REG_BASE + 0x60)
+/* HOST to ISH message registers */
+#define IPC_REG_HOST2ISH_MSG   (IPC_REG_BASE + 0xE0)
+/* REMAP2 to enable DMA (D3 RCR) */
+#define        IPC_REG_ISH_RMP2        (IPC_REG_BASE + 0x368)
+
+#define        IPC_REG_MAX             (IPC_REG_BASE + 0x400)
+
+/*** register bits - HISR ***/
+/* bit corresponds HOST2ISH interrupt in PISR and PIMR registers */
+#define IPC_INT_HOST2ISH_BIT            (1<<0)
+/***********************************/
+/*CHV_A0, CHV_B0*/
+/* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */
+#define IPC_INT_ISH2HOST_BIT_CHV_AB    (1<<3)
+/*BXT, CHV_K0*/
+/* bit corresponds ISH2HOST interrupt in PISR and PIMR registers */
+#define IPC_INT_ISH2HOST_BIT_BXT       (1<<0)
+/***********************************/
+
+/* bit corresponds ISH2HOST busy clear interrupt in PIMR register */
+#define IPC_INT_ISH2HOST_CLR_MASK_BIT  (1<<11)
+
+/* offset of ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */
+#define IPC_INT_ISH2HOST_CLR_OFFS      (0)
+
+/* bit corresponds ISH2HOST busy clear interrupt in IPC_BUSY_CLR register */
+#define IPC_INT_ISH2HOST_CLR_BIT       (1<<IPC_INT_ISH2HOST_CLR_OFFS)
+
+/* bit corresponds busy bit in doorbell registers */
+#define IPC_DRBL_BUSY_OFFS             (31)
+#define IPC_DRBL_BUSY_BIT              (1<<IPC_DRBL_BUSY_OFFS)
+
+#define        IPC_HOST_OWNS_MSG_OFFS          (30)
+
+/*
+ * A0: bit means that host owns MSGnn registers and is reading them.
+ * ISH FW may not write to them
+ */
+#define        IPC_HOST_OWNS_MSG_BIT           (1<<IPC_HOST_OWNS_MSG_OFFS)
+
+/*
+ * Host status bits (HOSTCOMM)
+ */
+/* bit corresponds host ready bit in Host Status Register (HOST_COMM) */
+#define IPC_HOSTCOMM_READY_OFFS                (7)
+#define IPC_HOSTCOMM_READY_BIT         (1<<IPC_HOSTCOMM_READY_OFFS)
+
+/***********************************/
+/*CHV_A0, CHV_B0*/
+#define        IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB (31)
+#define        IPC_HOSTCOMM_INT_EN_BIT_CHV_AB          \
+       (1<<IPC_HOSTCOMM_INT_EN_OFFS_CHV_AB)
+/*BXT, CHV_K0*/
+#define IPC_PIMR_INT_EN_OFFS_BXT       (0)
+#define IPC_PIMR_INT_EN_BIT_BXT                (1<<IPC_PIMR_INT_EN_OFFS_BXT)
+
+#define IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT   (8)
+#define IPC_HOST2ISH_BUSYCLEAR_MASK_BIT                \
+       (1<<IPC_HOST2ISH_BUSYCLEAR_MASK_OFFS_BXT)
+/***********************************/
+/*
+ * both Host and ISH have ILUP at bit 0
+ * bit corresponds host ready bit in both status registers
+ */
+#define IPC_ILUP_OFFS                  (0)
+#define IPC_ILUP_BIT                   (1<<IPC_ILUP_OFFS)
+
+/*
+ * FW status bits (relevant)
+ */
+#define        IPC_FWSTS_ILUP          0x1
+#define        IPC_FWSTS_ISHTP_UP      (1<<1)
+#define        IPC_FWSTS_DMA0          (1<<16)
+#define        IPC_FWSTS_DMA1          (1<<17)
+#define        IPC_FWSTS_DMA2          (1<<18)
+#define        IPC_FWSTS_DMA3          (1<<19)
+
+#define        IPC_ISH_IN_DMA          \
+       (IPC_FWSTS_DMA0 | IPC_FWSTS_DMA1 | IPC_FWSTS_DMA2 | IPC_FWSTS_DMA3)
+
+/* bit corresponds host ready bit in ISH FW Status Register */
+#define IPC_ISH_ISHTP_READY_OFFS               (1)
+#define IPC_ISH_ISHTP_READY_BIT                (1<<IPC_ISH_ISHTP_READY_OFFS)
+
+#define        IPC_RMP2_DMA_ENABLED    0x1     /* Value to enable DMA, per D3 
RCR */
+
+#define IPC_MSG_MAX_SIZE       0x80
+
+
+#define IPC_HEADER_LENGTH_MASK         0x03FF
+#define IPC_HEADER_PROTOCOL_MASK       0x0F
+#define IPC_HEADER_MNG_CMD_MASK                0x0F
+
+#define IPC_HEADER_LENGTH_OFFSET       0
+#define IPC_HEADER_PROTOCOL_OFFSET     10
+#define IPC_HEADER_MNG_CMD_OFFSET      16
+
+#define IPC_HEADER_GET_LENGTH(drbl_reg)                \
+       (((drbl_reg) >> IPC_HEADER_LENGTH_OFFSET)&IPC_HEADER_LENGTH_MASK)
+#define IPC_HEADER_GET_PROTOCOL(drbl_reg)      \
+       (((drbl_reg) >> IPC_HEADER_PROTOCOL_OFFSET)&IPC_HEADER_PROTOCOL_MASK)
+#define IPC_HEADER_GET_MNG_CMD(drbl_reg)       \
+       (((drbl_reg) >> IPC_HEADER_MNG_CMD_OFFSET)&IPC_HEADER_MNG_CMD_MASK)
+
+#define IPC_IS_BUSY(drbl_reg)                  \
+       (((drbl_reg)&IPC_DRBL_BUSY_BIT) == ((uint32_t)IPC_DRBL_BUSY_BIT))
+
+/***********************************/
+/*CHV_A0, CHV_B0*/
+#define IPC_INT_FROM_ISH_TO_HOST_CHV_AB(drbl_reg) \
+       (((drbl_reg)&IPC_INT_ISH2HOST_BIT_CHV_AB) == \
+       ((u32)IPC_INT_ISH2HOST_BIT_CHV_AB))
+/*BXT, CHV_K0*/
+#define IPC_INT_FROM_ISH_TO_HOST_BXT(drbl_reg) \
+       (((drbl_reg)&IPC_INT_ISH2HOST_BIT_BXT) == \
+       ((u32)IPC_INT_ISH2HOST_BIT_BXT))
+/***********************************/
+
+#define IPC_BUILD_HEADER(length, protocol, busy)               \
+       (((busy)<<IPC_DRBL_BUSY_OFFS) |                         \
+       ((protocol) << IPC_HEADER_PROTOCOL_OFFSET) |            \
+       ((length)<<IPC_HEADER_LENGTH_OFFSET))
+
+#define IPC_BUILD_MNG_MSG(cmd, length)                         \
+       (((1)<<IPC_DRBL_BUSY_OFFS)|                             \
+       ((IPC_PROTOCOL_MNG)<<IPC_HEADER_PROTOCOL_OFFSET)|       \
+       ((cmd)<<IPC_HEADER_MNG_CMD_OFFSET)|((length)<<IPC_HEADER_LENGTH_OFFSET))
+
+
+#define IPC_SET_HOST_READY(host_status)                \
+                               ((host_status) |= (IPC_HOSTCOMM_READY_BIT))
+
+#define IPC_SET_HOST_ILUP(host_status)         \
+                               ((host_status) |= (IPC_ILUP_BIT))
+
+#define IPC_CLEAR_HOST_READY(host_status)      \
+                               ((host_status) ^= (IPC_HOSTCOMM_READY_BIT))
+
+#define IPC_CLEAR_HOST_ILUP(host_status)       \
+                               ((host_status) ^= (IPC_ILUP_BIT))
+
+/* todo - temp until PIMR HW ready */
+#define IPC_HOST_BUSY_READING_OFFS     6
+
+/* bit corresponds host ready bit in Host Status Register (HOST_COMM) */
+#define IPC_HOST_BUSY_READING_BIT      (1<<IPC_HOST_BUSY_READING_OFFS)
+
+#define IPC_SET_HOST_BUSY_READING(host_status) \
+                               ((host_status) |= (IPC_HOST_BUSY_READING_BIT))
+
+#define IPC_CLEAR_HOST_BUSY_READING(host_status)\
+                               ((host_status) ^= (IPC_HOST_BUSY_READING_BIT))
+
+
+#define IPC_IS_ISH_ISHTP_READY(ish_status)     \
+               (((ish_status) & IPC_ISH_ISHTP_READY_BIT) ==    \
+                       ((uint32_t)IPC_ISH_ISHTP_READY_BIT))
+
+#define IPC_IS_ISH_ILUP(ish_status)            \
+               (((ish_status) & IPC_ILUP_BIT) == ((uint32_t)IPC_ILUP_BIT))
+
+
+#define IPC_PROTOCOL_ISHTP             1
+#define IPC_PROTOCOL_MNG               3
+
+#define MNG_RX_CMPL_ENABLE             0
+#define MNG_RX_CMPL_DISABLE            1
+#define MNG_RX_CMPL_INDICATION         2
+#define MNG_RESET_NOTIFY               3
+#define MNG_RESET_NOTIFY_ACK           4
+#define MNG_SYNC_FW_CLOCK              5
+#define MNG_ILLEGAL_CMD                        0xFF
+
+#endif /* _ISHTP_ISH_REGS_H_ */
+
diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h 
b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
new file mode 100644
index 0000000..21c3c5d3
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
@@ -0,0 +1,71 @@
+/*
+ * H/W layer of ISHTP provider device (ISH)
+ *
+ * Copyright (c) 2014-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _ISHTP_HW_ISH_H_
+#define _ISHTP_HW_ISH_H_
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include "hw-ish-regs.h"
+#include "ishtp-dev.h"
+
+#define CHV_DEVICE_ID          0x22D8
+#define BXT_Ax_DEVICE_ID       0x0AA2
+#define BXT_Bx_DEVICE_ID       0x1AA2
+#define BXTP_Ax_DEVICE_ID      0x5AA2
+#define SPT_Ax_DEVICE_ID       0x9D35
+
+#define        REVISION_ID_CHT_A0      0x6
+#define        REVISION_ID_CHT_Ax_SI   0x0
+#define        REVISION_ID_CHT_Bx_SI   0x10
+#define        REVISION_ID_CHT_Kx_SI   0x20
+#define        REVISION_ID_CHT_Dx_SI   0x30
+#define        REVISION_ID_CHT_B0      0xB0
+#define        REVISION_ID_SI_MASK     0x70
+
+struct ipc_rst_payload_type {
+       uint16_t        reset_id;
+       uint16_t        reserved;
+};
+
+struct time_sync_format {
+       uint8_t ts1_source;
+       uint8_t ts2_source;
+       uint16_t reserved;
+} __packed;
+
+struct ipc_time_update_msg {
+       uint64_t primary_host_time;
+       struct time_sync_format sync_info;
+       uint64_t secondary_host_time;
+} __packed;
+
+enum {
+       HOST_UTC_TIME_USEC = 0,
+       HOST_SYSTEM_TIME_USEC = 1
+};
+
+struct ish_hw {
+       void __iomem *mem_addr;
+};
+
+#define to_ish_hw(dev) (struct ish_hw *)((dev)->hw)
+
+irqreturn_t ish_irq_handler(int irq, void *dev_id);
+struct ishtp_device *ish_dev_init(struct pci_dev *pdev);
+int ish_hw_start(struct ishtp_device *dev);
+
+#endif /* _ISHTP_HW_ISH_H_ */
+
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c 
b/drivers/hid/intel-ish-hid/ipc/ipc.c
new file mode 100644
index 0000000..fdc242a
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ipc/ipc.c
@@ -0,0 +1,710 @@
+/*
+ * H/W layer of ISHTP provider device (ISH)
+ *
+ * Copyright (c) 2014-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include "client.h"
+#include "hw-ish.h"
+#include "utils.h"
+#include "hbm.h"
+
+/* For FW reset flow */
+static struct work_struct fw_reset_work;
+static struct ishtp_device *ishtp_dev;
+
+/* ish_reg_read - reads 32bit register */
+static inline uint32_t ish_reg_read(const struct ishtp_device *dev,
+       unsigned long offset)
+{
+       struct ish_hw *hw = to_ish_hw(dev);
+
+       return readl(hw->mem_addr + offset);
+}
+
+/* ish_reg_write - writes 32bit register */
+static inline void ish_reg_write(struct ishtp_device *dev, unsigned long 
offset,
+       uint32_t value)
+{
+       struct ish_hw *hw = to_ish_hw(dev);
+
+       writel(value, hw->mem_addr + offset);
+}
+
+static inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev)
+{
+       return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+}
+
+bool check_generated_interrupt(struct ishtp_device *dev)
+{
+       bool interrupt_generated = true;
+       uint32_t pisr_val = 0;
+
+       if (dev->pdev->device == CHV_DEVICE_ID) {
+               pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB);
+               interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
+       } else {
+               pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT);
+               interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_BXT(pisr_val);
+       }
+
+       return interrupt_generated;
+}
+
+/* ish_is_input_ready - check if ISH FW is ready for receiving data */
+static bool ish_is_input_ready(struct ishtp_device *dev)
+{
+       uint32_t doorbell_val;
+
+       doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL);
+       return !IPC_IS_BUSY(doorbell_val);
+}
+
+/* set_host_ready - set host ready indication to FW */
+void set_host_ready(struct ishtp_device *dev)
+{
+       if (dev->pdev->device == CHV_DEVICE_ID) {
+               if (dev->pdev->revision == REVISION_ID_CHT_A0 ||
+                               (dev->pdev->revision & REVISION_ID_SI_MASK) ==
+                               REVISION_ID_CHT_Ax_SI)
+                       ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81);
+               else if (dev->pdev->revision == REVISION_ID_CHT_B0 ||
+                               (dev->pdev->revision & REVISION_ID_SI_MASK) ==
+                               REVISION_ID_CHT_Bx_SI ||
+                               (dev->pdev->revision & REVISION_ID_SI_MASK) ==
+                               REVISION_ID_CHT_Kx_SI ||
+                               (dev->pdev->revision & REVISION_ID_SI_MASK) ==
+                               REVISION_ID_CHT_Dx_SI) {
+                       uint32_t host_comm_val;
+
+                       host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM);
+                       host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81;
+                       ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val);
+               }
+       } else {
+                       uint32_t host_pimr_val;
+
+                       host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT);
+                       host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT;
+                       /*
+                        * disable interrupt generated instead of
+                        * RX_complete_msg
+                        */
+                       host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT;
+
+                       ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val);
+       }
+}
+
+/* ishtp_fw_is_ready - check if the hw is ready */
+static bool ishtp_fw_is_ready(struct ishtp_device *dev)
+{
+       uint32_t ish_status = _ish_read_fw_sts_reg(dev);
+
+       return IPC_IS_ISH_ILUP(ish_status) &&
+               IPC_IS_ISH_ISHTP_READY(ish_status);
+}
+
+/* Indicate to FW that the host is ready */
+static void ish_set_host_rdy(struct ishtp_device *dev)
+{
+       uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM);
+
+       IPC_SET_HOST_READY(host_status);
+       ish_reg_write(dev, IPC_REG_HOST_COMM, host_status);
+}
+
+void ish_clr_host_rdy(struct ishtp_device *dev)
+{
+       uint32_t host_status = ish_reg_read(dev, IPC_REG_HOST_COMM);
+
+       IPC_CLEAR_HOST_READY(host_status);
+       ish_reg_write(dev, IPC_REG_HOST_COMM, host_status);
+}
+
+/* _ish_read_hdr - reads hdr of 32 bit length. */
+static uint32_t _ishtp_read_hdr(const struct ishtp_device *dev)
+{
+       return ish_reg_read(dev, IPC_REG_ISH2HOST_MSG);
+}
+
+/* ish_read - reads a message from ishtp device. */
+static int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer,
+       unsigned long buffer_length)
+{
+       uint32_t        i;
+       uint32_t        *r_buf = (uint32_t *)buffer;
+       uint32_t        msg_offs;
+
+       msg_offs = IPC_REG_ISH2HOST_MSG + sizeof(struct ishtp_msg_hdr);
+       for (i = 0; i < buffer_length; i += sizeof(uint32_t))
+               *r_buf++ = ish_reg_read(dev, msg_offs + i);
+
+       return 0;
+}
+
+/**
+ * write_ipc_from_queue() - try to write ipc msg from Tx queue to device
+ * @dev:       ishtp device pointer
+ *
+ * Check if DRBL is cleared. if it is - write the first IPC msg,  then call
+ * the callback function (unless it's NULL)
+ */
+int write_ipc_from_queue(struct ishtp_device *dev)
+{
+       struct wr_msg_ctl_info  *ipc_link;
+       unsigned long   length;
+       unsigned long   rem;
+       unsigned long   flags;
+       uint32_t        doorbell_val;
+       uint32_t        *r_buf;
+       uint32_t        reg_addr;
+       int     i;
+       void    (*ipc_send_compl)(void *);
+       void    *ipc_send_compl_prm;
+       static int      out_ipc_locked;
+       unsigned long   out_ipc_flags;
+
+       if (dev->dev_state == ISHTP_DEV_DISABLED)
+               return  -EINVAL;
+
+       spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags);
+       if (out_ipc_locked) {
+               spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
+               return -EBUSY;
+       }
+       out_ipc_locked = 1;
+       if (!ish_is_input_ready(dev)) {
+               out_ipc_locked = 0;
+               spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
+
+       spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
+       /*
+        * if tx send list is empty - return 0;
+        * may happen, as RX_COMPLETE handler doesn't check list emptiness.
+        */
+       if (list_empty(&dev->wr_processing_list_head.link)) {
+               spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
+               out_ipc_locked = 0;
+               return  0;
+       }
+
+       ipc_link = list_entry(dev->wr_processing_list_head.link.next,
+                             struct wr_msg_ctl_info, link);
+       /* first 4 bytes of the data is the doorbell value (IPC header) */
+       length = ipc_link->length - sizeof(uint32_t);
+       doorbell_val = *(uint32_t *)ipc_link->inline_data;
+       r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t));
+
+       /* If sending MNG_SYNC_FW_CLOCK, update clock again */
+       if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG &&
+               IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) {
+               struct timespec ts_system;
+               struct timeval tv_utc;
+               uint64_t        usec_system, usec_utc;
+               struct ipc_time_update_msg time_update;
+               struct time_sync_format ts_format;
+
+               get_monotonic_boottime(&ts_system);
+               do_gettimeofday(&tv_utc);
+               usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC;
+               usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 +
+                                               ((uint32_t)tv_utc.tv_usec);
+               ts_format.ts1_source = HOST_SYSTEM_TIME_USEC;
+               ts_format.ts2_source = HOST_UTC_TIME_USEC;
+
+               time_update.primary_host_time = usec_system;
+               time_update.secondary_host_time = usec_utc;
+               time_update.sync_info = ts_format;
+
+               memcpy(r_buf, &time_update, sizeof(struct ipc_time_update_msg));
+       }
+
+       for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++,
+                       reg_addr += 4)
+               ish_reg_write(dev, reg_addr, r_buf[i]);
+
+       rem = length & 0x3;
+       if (rem > 0) {
+               uint32_t reg = 0;
+
+               memcpy(&reg, &r_buf[length >> 2], rem);
+               ish_reg_write(dev, reg_addr, reg);
+       }
+       /* Flush writes to msg registers and doorbell */
+       ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+       /* Update IPC counters */
+       ++dev->ipc_tx_cnt;
+       dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val);
+
+       ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val);
+       out_ipc_locked = 0;
+
+       ipc_send_compl = ipc_link->ipc_send_compl;
+       ipc_send_compl_prm = ipc_link->ipc_send_compl_prm;
+       list_del_init(&ipc_link->link);
+       list_add_tail(&ipc_link->link, &dev->wr_free_list_head.link);
+       spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
+
+       /*
+        * callback will be called out of spinlock,
+        * after ipc_link returned to free list
+        */
+       if (ipc_send_compl)
+               ipc_send_compl(ipc_send_compl_prm);
+
+       return 0;
+}
+
+/**
+ * write_ipc_to_queue() - write ipc msg to Tx queue
+ * @dev:               ishtp device instance
+ * @ipc_send_compl:    Send complete callback
+ * @ipc_send_compl_prm:        Parameter to send in complete callback
+ * @msg:               Pointer to message
+ * @length:            Length of message
+ *
+ * Recived msg with IPC (and upper protocol) header  and add it to the device
+ *  Tx-to-write list then try to send the first IPC waiting msg
+ *  (if DRBL is cleared)
+ * This function returns negative value for failure (means free list
+ *  is empty, or msg too long) and 0 for success.
+ */
+static int write_ipc_to_queue(struct ishtp_device *dev,
+       void (*ipc_send_compl)(void *), void *ipc_send_compl_prm,
+       unsigned char *msg, int length)
+{
+       struct wr_msg_ctl_info *ipc_link;
+       unsigned long   flags;
+
+       if (length > IPC_FULL_MSG_SIZE)
+               return -EMSGSIZE;
+
+       spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
+       if (list_empty(&dev->wr_free_list_head.link)) {
+               spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
+               return -ENOMEM;
+       }
+       ipc_link = list_entry(dev->wr_free_list_head.link.next,
+               struct wr_msg_ctl_info, link);
+       list_del_init(&ipc_link->link);
+
+       ipc_link->ipc_send_compl = ipc_send_compl;
+       ipc_link->ipc_send_compl_prm = ipc_send_compl_prm;
+       ipc_link->length = length;
+       memcpy(ipc_link->inline_data, msg, length);
+
+       list_add_tail(&ipc_link->link, &dev->wr_processing_list_head.link);
+       spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
+
+       write_ipc_from_queue(dev);
+
+       return 0;
+}
+
+/* Send a bus management message */
+static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code,
+       void *msg, size_t size)
+{
+       unsigned char   ipc_msg[IPC_FULL_MSG_SIZE];
+       uint32_t        drbl_val = IPC_BUILD_MNG_MSG(msg_code, size);
+
+       memcpy(ipc_msg, &drbl_val, sizeof(uint32_t));
+       memcpy(ipc_msg + sizeof(uint32_t), msg, size);
+       return  write_ipc_to_queue(dev, NULL, NULL, ipc_msg,
+               sizeof(uint32_t) + size);
+}
+
+static int ish_fw_reset_handler(struct ishtp_device *dev)
+{
+       uint32_t        reset_id;
+       unsigned long   flags;
+       struct wr_msg_ctl_info *processing, *next;
+
+       /* Read reset ID */
+       reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
+
+       /* Clear IPC output queue */
+       spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
+       list_for_each_entry_safe(processing, next,
+                       &dev->wr_processing_list_head.link, link) {
+               list_del(&processing->link);
+               list_add_tail(&processing->link, &dev->wr_free_list_head.link);
+       }
+       spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
+
+       /* ISHTP notification in IPC_RESET */
+       ishtp_reset_handler(dev);
+
+       if (!ish_is_input_ready(dev))
+               timed_wait_for_timeout(WAIT_FOR_SEND_SLICE,
+                       ish_is_input_ready(dev), (2 * HZ));
+
+       /* ISH FW is dead */
+       if (!ish_is_input_ready(dev))
+               return  -EPIPE;
+       /*
+        * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
+        * RESET_NOTIFY_ACK - FW will be checking for it
+        */
+       ish_set_host_rdy(dev);
+       /* Send RESET_NOTIFY_ACK (with reset_id) */
+       ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id,
+                        sizeof(uint32_t));
+
+       /* Wait for ISH FW'es ILUP and ISHTP_READY */
+       timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev),
+               (2 * HZ));
+       if (!ishtp_fw_is_ready(dev)) {
+               /* ISH FW is dead */
+               uint32_t        ish_status;
+
+               ish_status = _ish_read_fw_sts_reg(dev);
+               dev_err(dev->devc,
+                       "[ishtp-ish]: completed reset, ISH is dead "
+                       "(FWSTS = %08X)\n",
+                       ish_status);
+               return -ENODEV;
+       }
+       return  0;
+}
+
+static void fw_reset_work_fn(struct work_struct *unused)
+{
+       int     rv;
+
+       rv = ish_fw_reset_handler(ishtp_dev);
+       if (!rv) {
+               /* ISH is ILUP & ISHTP-ready. Restart ISHTP */
+               schedule_timeout(HZ / 3);
+               ishtp_dev->recvd_hw_ready = 1;
+               wake_up(&ishtp_dev->wait_hw_ready);
+
+               /* ISHTP notification in IPC_RESET sequence completion */
+               ishtp_reset_compl_handler(ishtp_dev);
+       } else
+               dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n",
+                       rv);
+}
+
+static void _ish_sync_fw_clock(struct ishtp_device *dev)
+{
+       static unsigned long    prev_sync;
+       struct timespec ts;
+       uint64_t        usec;
+
+       if (prev_sync && jiffies - prev_sync < 20 * HZ)
+               return;
+
+       prev_sync = jiffies;
+       get_monotonic_boottime(&ts);
+       usec = (timespec_to_ns(&ts)) / NSEC_PER_USEC;
+       ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t));
+}
+
+/*
+ * recv_ipc() - Receive and process IPC management messages
+ * @dev:               ishtp device instance
+ * @doorbell_val:      doorbell value
+ *
+ * This function runs in ISR context.
+ * NOTE: Any other mng command than reset_notify and reset_notify_ack
+ * won't wake BH handler
+ */
+static void    recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val)
+{
+       uint32_t        mng_cmd;
+
+       mng_cmd = IPC_HEADER_GET_MNG_CMD(doorbell_val);
+
+       switch (mng_cmd) {
+       default:
+               break;
+
+       case MNG_RX_CMPL_INDICATION:
+               if (dev->suspend_flag) {
+                       dev->suspend_flag = 0;
+                       wake_up(&dev->suspend_wait);
+               }
+               write_ipc_from_queue(dev);
+               break;
+
+       case MNG_RESET_NOTIFY:
+               if (!ishtp_dev) {
+                       ishtp_dev = dev;
+                       INIT_WORK(&fw_reset_work, fw_reset_work_fn);
+               }
+               schedule_work(&fw_reset_work);
+               break;
+
+       case MNG_RESET_NOTIFY_ACK:
+               dev->recvd_hw_ready = 1;
+               wake_up(&dev->wait_hw_ready);
+               break;
+       }
+}
+
+/* ish_irq_handler - ISR of the ISHTP device */
+irqreturn_t ish_irq_handler(int irq, void *dev_id)
+{
+       struct ishtp_device     *dev = dev_id;
+       uint32_t        doorbell_val;
+       bool    interrupt_generated;
+
+       /* Check that it's interrupt from ISH (may be shared) */
+       interrupt_generated = check_generated_interrupt(dev);
+
+       if (!interrupt_generated)
+               return IRQ_NONE;
+
+       doorbell_val = ish_reg_read(dev, IPC_REG_ISH2HOST_DRBL);
+       if (!IPC_IS_BUSY(doorbell_val))
+               return IRQ_HANDLED;
+
+       if (dev->dev_state == ISHTP_DEV_DISABLED)
+               return  IRQ_HANDLED;
+
+       /* Sanity check: IPC dgram length in header */
+       if (IPC_HEADER_GET_LENGTH(doorbell_val) > IPC_PAYLOAD_SIZE) {
+               dev_err(dev->devc,
+                       "IPC hdr - bad length: %u; dropped\n",
+                       (unsigned int)IPC_HEADER_GET_LENGTH(doorbell_val));
+               goto    eoi;
+       }
+
+       switch (IPC_HEADER_GET_PROTOCOL(doorbell_val)) {
+       default:
+               break;
+       case IPC_PROTOCOL_MNG:
+               recv_ipc(dev, doorbell_val);
+               break;
+       case IPC_PROTOCOL_ISHTP:
+               ishtp_recv(dev);
+               break;
+       }
+
+eoi:
+       /* Update IPC counters */
+       ++dev->ipc_rx_cnt;
+       dev->ipc_rx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val);
+
+       ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0);
+       /* Flush write to doorbell */
+       ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+       return  IRQ_HANDLED;
+}
+
+static int _ish_hw_reset(struct ishtp_device *dev)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int     rv;
+       unsigned int    dma_delay;
+       uint16_t csr;
+
+       if (!pdev)
+               return  -ENODEV;
+
+       rv = pci_reset_function(pdev);
+       if (!rv)
+               dev->dev_state = ISHTP_DEV_RESETTING;
+
+       if (!pdev->pm_cap) {
+               dev_err(&pdev->dev, "Can't reset - no PM caps\n");
+               return  -EINVAL;
+       }
+
+       /* Now trigger reset to FW */
+       ish_reg_write(dev, IPC_REG_ISH_RMP2, 0);
+
+       for (dma_delay = 0; dma_delay < MAX_DMA_DELAY &&
+               _ish_read_fw_sts_reg(dev) & (IPC_ISH_IN_DMA);
+               dma_delay += 5)
+               mdelay(5);
+
+       if (dma_delay >= MAX_DMA_DELAY) {
+               dev_err(&pdev->dev,
+                       "Can't reset - stuck with DMA in-progress\n");
+               return  -EBUSY;
+       }
+
+       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &csr);
+
+       csr &= ~PCI_PM_CTRL_STATE_MASK;
+       csr |= PCI_D3hot;
+       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
+
+       mdelay(pdev->d3_delay);
+
+       csr &= ~PCI_PM_CTRL_STATE_MASK;
+       csr |= PCI_D0;
+       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
+
+       ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED);
+
+       /*
+        * Send 0 IPC message so that ISH FW wakes up if it was already
+        * asleep
+        */
+       ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT);
+
+       /* Flush writes to doorbell and REMAP2 */
+       ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+       return  0;
+}
+
+/* _ish_ipc_reset - resets host and fw IPC and upper layers. */
+static int _ish_ipc_reset(struct ishtp_device *dev)
+{
+       struct ipc_rst_payload_type ipc_mng_msg;
+       int     rv = 0;
+
+       ipc_mng_msg.reset_id = 1;
+       ipc_mng_msg.reserved = 0;
+
+       set_host_ready(dev);
+
+       /* Clear the incoming doorbell */
+       ish_reg_write(dev, IPC_REG_ISH2HOST_DRBL, 0);
+       /* Flush write to doorbell */
+       ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+       dev->recvd_hw_ready = 0;
+
+       /* send message */
+       rv = ipc_send_mng_msg(dev, MNG_RESET_NOTIFY, &ipc_mng_msg,
+               sizeof(struct ipc_rst_payload_type));
+       if (rv) {
+               dev_err(dev->devc, "Failed to send IPC MNG_RESET_NOTIFY\n");
+               return  rv;
+       }
+
+       wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, 2 * HZ);
+       if (!dev->recvd_hw_ready) {
+               dev_err(dev->devc, "Timed out waiting for HW ready\n");
+               rv = -ENODEV;
+       }
+
+       return rv;
+}
+
+int ish_hw_start(struct ishtp_device *dev)
+{
+       ish_set_host_rdy(dev);
+       /* After that we can enable ISH DMA operation */
+       ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED);
+
+       /*
+        * Send 0 IPC message so that ISH FW wakes up if it was already
+        * asleep
+        */
+       ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT);
+       /* Flush write to doorbell */
+       ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+       set_host_ready(dev);
+
+       /* wait for FW-initiated reset flow */
+       if (!dev->recvd_hw_ready)
+               wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready,
+                       10 * HZ);
+
+       if (!dev->recvd_hw_ready) {
+               dev_err(dev->devc,
+                       "[ishtp-ish]: Timed out waiting for "
+                       "FW-initiated reset\n");
+               return  -ENODEV;
+       }
+
+       return 0;
+}
+
+static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length,
+                                  int busy)
+{
+       uint32_t drbl_val;
+
+       drbl_val = IPC_BUILD_HEADER(length, IPC_PROTOCOL_ISHTP, busy);
+
+       return drbl_val;
+}
+
+static const struct ishtp_hw_ops ish_hw_ops = {
+       .hw_reset = _ish_hw_reset,
+       .ipc_reset = _ish_ipc_reset,
+       .ipc_get_header = ish_ipc_get_header,
+       .ishtp_read = _ishtp_read,
+       .write = write_ipc_to_queue,
+       .get_fw_status = _ish_read_fw_sts_reg,
+       .sync_fw_clock = _ish_sync_fw_clock,
+       .ishtp_read_hdr = _ishtp_read_hdr
+};
+
+struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
+{
+       struct ishtp_device *dev;
+       int     i;
+
+       dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw),
+               GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       ishtp_device_init(dev);
+
+       init_waitqueue_head(&dev->wait_hw_ready);
+
+       spin_lock_init(&dev->wr_processing_spinlock);
+       spin_lock_init(&dev->out_ipc_spinlock);
+
+       /* Init IPC processing and free lists */
+       INIT_LIST_HEAD(&dev->wr_processing_list_head.link);
+       INIT_LIST_HEAD(&dev->wr_free_list_head.link);
+       for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
+               struct wr_msg_ctl_info  *tx_buf;
+
+               tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL);
+               if (!tx_buf) {
+                       /*
+                        * IPC buffers may be limited or not available
+                        * at all - although this shouldn't happen
+                        */
+                       dev_err(dev->devc,
+                               "[ishtp-ish]: failure in Tx FIFO "
+                               "allocations (%d)\n", i);
+                       break;
+               }
+               list_add_tail(&tx_buf->link, &dev->wr_free_list_head.link);
+       }
+
+       dev->ops = &ish_hw_ops;
+       dev->devc = &pdev->dev;
+       dev->mtu = IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr);
+       return dev;
+}
+
+void   ishtp_device_disable(struct ishtp_device *dev)
+{
+       dev->dev_state = ISHTP_DEV_DISABLED;
+       ish_clr_host_rdy(dev);
+       kfree(dev->fw_clients);
+}
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c 
b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
new file mode 100644
index 0000000..e4d00d2
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -0,0 +1,238 @@
+/*
+ * PCI glue for ISHTP provider device (ISH) driver
+ *
+ * Copyright (c) 2014-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/miscdevice.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/intel_ish.h>
+#include "ishtp-dev.h"
+#include "hw-ish.h"
+
+static const struct pci_device_id ish_pci_tbl[] = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXTP_Ax_DEVICE_ID)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
+       {0, }
+};
+MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
+
+static void ish_event_tracer(struct ishtp_device *dev, char *format, ...)
+{
+       if (trace_ishtp_dump_enabled()) {
+               va_list args;
+               char tmp_buf[100];
+
+               va_start(args, format);
+               vsnprintf(tmp_buf, sizeof(tmp_buf), format, args);
+               va_end(args);
+
+               trace_ishtp_dump(tmp_buf);
+       }
+}
+
+static int ish_init(struct ishtp_device *dev)
+{
+       int ret;
+
+       dev_set_drvdata(dev->devc, dev);
+
+       init_waitqueue_head(&dev->suspend_wait);
+
+       /* Register ishtp bus */
+       ret = ishtp_cl_bus_init();
+       if (ret) {
+               dev_err(dev->devc, "ISH: Init hw failed.\n");
+               return ret;
+       }
+
+       /* Set the state of ISH HW to start */
+       ret = ish_hw_start(dev);
+       if (ret) {
+               dev_err(dev->devc, "ISH: Init hw failed.\n");
+               goto bus_unreg;
+       }
+
+       /* Start the inter process communication to ISH processor */
+       ret = ishtp_start(dev);
+       if (ret) {
+               dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
+               goto bus_unreg;
+       }
+
+       return 0;
+
+bus_unreg:
+       ishtp_cl_bus_exit();
+
+       return ret;
+}
+
+static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct ishtp_device *dev;
+       struct ish_hw *hw;
+       int     ret;
+
+       /* enable pci dev */
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n");
+               return ret;
+       }
+
+       /* set PCI host mastering */
+       pci_set_master(pdev);
+
+       /* pci request regions for ISH driver */
+       ret = pci_request_regions(pdev, KBUILD_MODNAME);
+       if (ret) {
+               dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n");
+               goto disable_device;
+       }
+
+       /* allocates and initializes the ISH dev structure */
+       dev = ish_dev_init(pdev);
+       if (!dev) {
+               ret = -ENOMEM;
+               goto release_regions;
+       }
+       hw = to_ish_hw(dev);
+       dev->print_log = ish_event_tracer;
+
+       /* mapping IO device memory */
+       hw->mem_addr = pci_iomap(pdev, 0, 0);
+       if (!hw->mem_addr) {
+               dev_err(&pdev->dev, "ISH: mapping I/O range failure\n");
+               ret = -ENOMEM;
+               goto free_device;
+       }
+
+       dev->pdev = pdev;
+
+       /*
+        * PCI quirk: prevent from being put into D3 state. ISH has internal
+        * power management logic to transition to low power state based
+        * on the usage. So no explicit action is required to change the
+        * state to D3.
+        */
+       pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+
+
+       /* request and enable interrupt */
+       ret = request_irq(pdev->irq, ish_irq_handler, IRQF_NO_SUSPEND,
+                         KBUILD_MODNAME, dev);
+       if (ret) {
+               dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n",
+                       pdev->irq);
+               goto free_device;
+       }
+
+       ret = ish_init(dev);
+       if (ret)
+               goto free_device;
+
+       return 0;
+
+free_device:
+       pci_iounmap(pdev, hw->mem_addr);
+       kfree(dev);
+release_regions:
+       pci_release_regions(pdev);
+disable_device:
+       pci_disable_device(pdev);
+       dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");
+
+       return ret;
+}
+
+static int ish_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ishtp_device *dev = pci_get_drvdata(pdev);
+
+       enable_irq_wake(pdev->irq);
+       /*
+        * If previous suspend hasn't been asnwered then ISH is likely dead,
+        * don't attempt nested notification
+        */
+       if (dev->suspend_flag)
+               return  0;
+
+       dev->suspend_flag = 1;
+       ishtp_send_suspend(dev);
+
+       /* 25 ms should be enough for live ISH to flush all IPC buf */
+       if (dev->suspend_flag)
+               wait_event_timeout(dev->suspend_wait, !dev->suspend_flag,
+                                  msecs_to_jiffies(25));
+
+       return 0;
+}
+
+static int ish_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ishtp_device *dev = pci_get_drvdata(pdev);
+
+       disable_irq_wake(pdev->irq);
+       ishtp_send_resume(dev);
+       dev->suspend_flag = 0;
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ish_pm_ops = {
+       .suspend = ish_suspend,
+       .resume = ish_resume,
+};
+#define ISHTP_ISH_PM_OPS       (&ish_pm_ops)
+#else
+#define ISHTP_ISH_PM_OPS       NULL
+#endif
+
+static struct pci_driver ish_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = ish_pci_tbl,
+       .probe = ish_probe,
+       .driver.pm = ISHTP_ISH_PM_OPS,
+};
+
+static int __init ish_driver_init(void)
+{
+       return pci_register_driver(&ish_driver);
+}
+device_initcall(ish_driver_init);
+
+/* Primary author */
+MODULE_AUTHOR("Daniel Drubin <daniel.dru...@intel.com>");
+/* Adoption to upstream Linux kernel */
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruv...@linux.intel.com>");
+
+MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h 
b/drivers/hid/intel-ish-hid/ipc/utils.h
new file mode 100644
index 0000000..cc646de
--- /dev/null
+++ b/drivers/hid/intel-ish-hid/ipc/utils.h
@@ -0,0 +1,65 @@
+/*
+ * Utility macros of ISH
+ *
+ * Copyright (c) 2014-2016, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+#ifndef UTILS__H
+#define UTILS__H
+
+#define        WAIT_FOR_SEND_SLICE     (HZ / 10)
+#define        WAIT_FOR_CONNECT_SLICE  (HZ / 10)
+
+/*
+ * Waits for specified event when a thread that triggers event can't signal
+ * Also, waits *at_least* `timeinc` after condition is satisfied
+ */
+#define        timed_wait_for(timeinc, condition)                      \
+       do {                                                    \
+               int completed = 0;                              \
+               do {                                            \
+                       unsigned long   j;                      \
+                       int     done = 0;                       \
+                                                               \
+                       completed = (condition);                \
+                       for (j = jiffies, done = 0; !done; ) {  \
+                               schedule_timeout(timeinc);      \
+                               if (time_is_before_eq_jiffies(j + timeinc)) \
+                                       done = 1;               \
+                       }                                       \
+               } while (!(completed));                         \
+       } while (0)
+
+
+/*
+ * Waits for specified event when a thread that triggers event
+ * can't signal with timeout (use whenever we may hang)
+ */
+#define        timed_wait_for_timeout(timeinc, condition, timeout)     \
+       do {                                                    \
+               int     t = timeout;                            \
+               do {                                            \
+                       unsigned long   j;                      \
+                       int     done = 0;                       \
+                                                               \
+                       for (j = jiffies, done = 0; !done; ) {  \
+                               schedule_timeout(timeinc);      \
+                               if (time_is_before_eq_jiffies(j + timeinc)) \
+                                       done = 1;               \
+                       } \
+                       t -= timeinc;                           \
+                       if (t <= 0)                             \
+                               break;                          \
+               } while (!(condition));                         \
+       } while (0)
+
+#endif /* UTILS__H */
+
diff --git a/include/trace/events/intel_ish.h b/include/trace/events/intel_ish.h
new file mode 100644
index 0000000..92f7d5b
--- /dev/null
+++ b/include/trace/events/intel_ish.h
@@ -0,0 +1,30 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_ish
+
+#if !defined(_TRACE_INTEL_ISH_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_INTEL_ISH_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(ishtp_dump,
+
+       TP_PROTO(const char *message),
+
+       TP_ARGS(message),
+
+       TP_STRUCT__entry(
+               __string(message, message)
+       ),
+
+       TP_fast_assign(
+               __assign_str(message, message);
+       ),
+
+       TP_printk("%s", __get_str(message))
+);
+
+
+#endif /* _TRACE_INTEL_ISH_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
-- 
1.9.1

Reply via email to