Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae02e5d4 authored by Srinivas Pandruvada's avatar Srinivas Pandruvada Committed by Jiri Kosina
Browse files

HID: intel-ish-hid: ipc layer



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

Original-author: Daniel Drubin <daniel.drubin@intel.com>
Reviewed-and-tested-by: default avatarOoi, Joyce <joyce.ooi@intel.com>
Tested-by: default avatarGrant Likely <grant.likely@secretlab.ca>
Tested-by: default avatarRann Bar-On <rb6@duke.edu>
Tested-by: default avatarAtri Bhattacharya <badshah400@aim.com>
Signed-off-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 3703f53b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -10,3 +10,9 @@ intel-ishtp-objs += ishtp/client.o
intel-ishtp-objs += ishtp/bus.o
intel-ishtp-objs += ishtp/dma-if.o
intel-ishtp-objs += ishtp/client-buffers.o

obj-$(CONFIG_INTEL_ISH_HID) += 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
+220 −0
Original line number Diff line number Diff line
/*
 * 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_ */
+71 −0
Original line number Diff line number Diff line
/*
 * 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 APL_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);
void ish_device_disable(struct ishtp_device *dev);

#endif /* _ISHTP_HW_ISH_H_ */
+882 −0

File added.

Preview size limit exceeded, changes collapsed.

+333 −0
Original line number Diff line number Diff line
/*
 * 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, APL_Ax_DEVICE_ID)},
	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
	{0, }
};
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);

/**
 * ish_event_tracer() - Callback function to dump trace messages
 * @dev:	ishtp device
 * @format:	printf style format
 *
 * Callback to direct log messages to Linux trace buffers
 */
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);
	}
}

/**
 * ish_init() - Init function
 * @dev:	ishtp device
 *
 * This function initialize wait queues for suspend/resume and call
 * calls hadware initialization function. This will initiate
 * startup sequence
 *
 * Return: 0 for success or error code for failure
 */
static int ish_init(struct ishtp_device *dev)
{
	int ret;

	/* Set the state of ISH HW to start */
	ret = ish_hw_start(dev);
	if (ret) {
		dev_err(dev->devc, "ISH: hw start failed.\n");
		return ret;
	}

	/* Start the inter process communication to ISH processor */
	ret = ishtp_start(dev);
	if (ret) {
		dev_err(dev->devc, "ISHTP: Protocol init failed.\n");
		return ret;
	}

	return 0;
}

/**
 * ish_probe() - PCI driver probe callback
 * @pdev:	pci device
 * @ent:	pci device id
 *
 * Initialize PCI function, setup interrupt and call for ISH initialization
 *
 * Return: 0 for success or error code for failure
 */
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;

	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;
	}

	dev_set_drvdata(dev->devc, dev);

	init_waitqueue_head(&dev->suspend_wait);
	init_waitqueue_head(&dev->resume_wait);

	ret = ish_init(dev);
	if (ret)
		goto free_irq;

	return 0;

free_irq:
	free_irq(pdev->irq, dev);
free_device:
	pci_iounmap(pdev, hw->mem_addr);
	kfree(dev);
release_regions:
	pci_release_regions(pdev);
disable_device:
	pci_clear_master(pdev);
	pci_disable_device(pdev);
	dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");

	return ret;
}

/**
 * ish_remove() - PCI driver remove callback
 * @pdev:	pci device
 *
 * This function does cleanup of ISH on pci remove callback
 */
static void ish_remove(struct pci_dev *pdev)
{
	struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
	struct ish_hw *hw = to_ish_hw(ishtp_dev);

	ishtp_bus_remove_all_clients(ishtp_dev, false);
	ish_device_disable(ishtp_dev);

	free_irq(pdev->irq, ishtp_dev);
	pci_iounmap(pdev, hw->mem_addr);
	pci_release_regions(pdev);
	pci_clear_master(pdev);
	pci_disable_device(pdev);
	kfree(ishtp_dev);
}

static struct device *ish_resume_device;

/**
 * ish_resume_handler() - Work function to complete resume
 * @work:	work struct
 *
 * The resume work function to complete resume function asynchronously.
 * There are two types of platforms, one where ISH is not powered off,
 * in that case a simple resume message is enough, others we need
 * a reset sequence.
 */
static void ish_resume_handler(struct work_struct *work)
{
	struct pci_dev *pdev = to_pci_dev(ish_resume_device);
	struct ishtp_device *dev = pci_get_drvdata(pdev);
	int ret;

	ishtp_send_resume(dev);

	/* 50 ms to get resume response */
	if (dev->resume_flag)
		ret = wait_event_interruptible_timeout(dev->resume_wait,
						       !dev->resume_flag,
						       msecs_to_jiffies(50));

	/*
	 * If no resume response. This platform  is not S0ix compatible
	 * So on resume full reboot of ISH processor will happen, so
	 * need to go through init sequence again
	 */
	if (dev->resume_flag)
		ish_init(dev);
}

/**
 * ish_suspend() - ISH suspend callback
 * @device:	device pointer
 *
 * ISH suspend callback
 *
 * Return: 0 to the pm core
 */
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->resume_flag = 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_interruptible_timeout(dev->suspend_wait,
						 !dev->suspend_flag,
						  msecs_to_jiffies(25));

	return 0;
}

static DECLARE_WORK(resume_work, ish_resume_handler);
/**
 * ish_resume() - ISH resume callback
 * @device:	device pointer
 *
 * ISH resume callback
 *
 * Return: 0 to the pm core
 */
static int ish_resume(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);
	struct ishtp_device *dev = pci_get_drvdata(pdev);

	ish_resume_device = device;
	dev->resume_flag = 1;

	disable_irq_wake(pdev->irq);
	schedule_work(&resume_work);

	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,
	.remove = ish_remove,
	.driver.pm = ISHTP_ISH_PM_OPS,
};

static int __init ish_driver_init(void)
{
	return pci_register_driver(&ish_driver);
}

static void __exit ish_driver_exit(void)
{
	pci_unregister_driver(&ish_driver);
}

module_init(ish_driver_init);
module_exit(ish_driver_exit);

/* Original author */
MODULE_AUTHOR("Daniel Drubin <daniel.drubin@intel.com>");
/* Adoption to upstream Linux kernel */
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");

MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
MODULE_LICENSE("GPL");
Loading