Merge tag 'pci-v5.10-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / platform / x86 / intel_scu_ipc.c
index 3d7da52..d9cf7f7 100644 (file)
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/pm.h>
-#include <linux/sfi.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
 
-#include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
 /* IPC defines the following message types */
 #define IPC_IOC                  0x100         /* IPC command register IOC bit */
 
 struct intel_scu_ipc_dev {
-       struct device *dev;
+       struct device dev;
+       struct resource mem;
+       struct module *owner;
+       int irq;
        void __iomem *ipc_base;
        struct completion cmd_complete;
-       u8 irq_mode;
 };
 
-static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
-
 #define IPC_STATUS             0x04
 #define IPC_STATUS_IRQ         BIT(2)
 #define IPC_STATUS_ERR         BIT(1)
@@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
 /* Timeout in jiffies */
 #define IPC_TIMEOUT            (3 * HZ)
 
+static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
 static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
 
+static struct class intel_scu_ipc_class = {
+       .name = "intel_scu_ipc",
+       .owner = THIS_MODULE,
+};
+
+/**
+ * intel_scu_ipc_dev_get() - Get SCU IPC instance
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Call intel_scu_ipc_dev_put() to release the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
+{
+       struct intel_scu_ipc_dev *scu = NULL;
+
+       mutex_lock(&ipclock);
+       if (ipcdev) {
+               get_device(&ipcdev->dev);
+               /*
+                * Prevent the IPC provider from being unloaded while it
+                * is being used.
+                */
+               if (!try_module_get(ipcdev->owner))
+                       put_device(&ipcdev->dev);
+               else
+                       scu = ipcdev;
+       }
+
+       mutex_unlock(&ipclock);
+       return scu;
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
+
+/**
+ * intel_scu_ipc_dev_put() - Put SCU IPC instance
+ * @scu: SCU IPC instance
+ *
+ * This function releases the SCU IPC instance retrieved from
+ * intel_scu_ipc_dev_get() and allows the driver providing IPC to be
+ * unloaded.
+ */
+void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
+{
+       if (scu) {
+               module_put(scu->owner);
+               put_device(&scu->dev);
+       }
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
+
+struct intel_scu_ipc_devres {
+       struct intel_scu_ipc_dev *scu;
+};
+
+static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
+{
+       struct intel_scu_ipc_devres *dr = res;
+       struct intel_scu_ipc_dev *scu = dr->scu;
+
+       intel_scu_ipc_dev_put(scu);
+}
+
+/**
+ * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
+ * @dev: Device requesting the SCU IPC device
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
+{
+       struct intel_scu_ipc_devres *dr;
+       struct intel_scu_ipc_dev *scu;
+
+       dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return NULL;
+
+       scu = intel_scu_ipc_dev_get();
+       if (!scu) {
+               devres_free(dr);
+               return NULL;
+       }
+
+       dr->scu = scu;
+       devres_add(dev, dr);
+
+       return scu;
+}
+EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
+
 /*
  * Send ipc command
  * Command Register (Write Only):
@@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
                usleep_range(50, 100);
        } while (time_before(jiffies, end));
 
-       dev_err(scu->dev, "IPC timed out");
        return -ETIMEDOUT;
 }
 
@@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 {
        int status;
 
-       if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
-               dev_err(scu->dev, "IPC timed out\n");
+       if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
                return -ETIMEDOUT;
-       }
 
        status = ipc_read_status(scu);
        if (status & IPC_STATUS_ERR)
@@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 
 static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
 {
-       return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
+       return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
 }
 
 /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
-static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
+static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                       u32 count, u32 op, u32 id)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
        int nc;
        u32 offset = 0;
        int err;
@@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
        memset(cbuf, 0, sizeof(cbuf));
 
        mutex_lock(&ipclock);
-
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
@@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 }
 
 /**
- * intel_scu_ipc_ioread8               -       read a word via the SCU
+ * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Return pointer for read byte
  *
@@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
  *
  * This function may sleep.
  */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data)
+int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
 {
-       return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+       return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_ioread8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
 
 /**
- * intel_scu_ipc_iowrite8              -       write a byte via the SCU
+ * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Byte to write
  *
@@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data)
+int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
 {
-       return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+       return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
 
 /**
- * intel_scu_ipc_readvv                -       read a set of registers
+ * intel_scu_ipc_dev_readv() - Read a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to return
  * @len: Length of array
@@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                           size_t len)
 {
-       return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+       return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_readv);
+EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
 
 /**
- * intel_scu_ipc_writev                -       write a set of registers
+ * intel_scu_ipc_dev_writev() - Write a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to write
  * @len: Length of array
@@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                            size_t len)
 {
-       return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+       return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_writev);
+EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
 
 /**
- * intel_scu_ipc_update_register       -       r/m/w a register
+ * intel_scu_ipc_dev_update() - Update a register
+ * @scu: Optional SCU IPC instance
  * @addr: Register address
- * @bits: Bits to update
+ * @data: Bits to update
  * @mask: Mask of bits to update
  *
  * Read-modify-write power control unit register. The first data argument
@@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
  * This function may sleep. Locking between SCU accesses is handled
  * for the caller.
  */
-int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
+int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
+                            u8 mask)
 {
-       u8 data[2] = { bits, mask };
-       return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
+       u8 tmp[2] = { data, mask };
+       return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
 }
-EXPORT_SYMBOL(intel_scu_ipc_update_register);
+EXPORT_SYMBOL(intel_scu_ipc_dev_update);
 
 /**
- * intel_scu_ipc_simple_command        -       send a simple command
+ * intel_scu_ipc_dev_simple_command() - Send a simple command
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  *
@@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
  * This function may sleep. Locking for SCU accesses is handled for the
  * caller.
  */
-int intel_scu_ipc_simple_command(int cmd, int sub)
+int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
+                                    int sub)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       u32 cmdval;
        int err;
 
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
-       ipc_command(scu, sub << 12 | cmd);
+       scu = ipcdev;
+       cmdval = sub << 12 | cmd;
+       ipc_command(scu, cmdval);
        err = intel_scu_ipc_check_status(scu);
        mutex_unlock(&ipclock);
+       if (err)
+               dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
        return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
 
 /**
- * intel_scu_ipc_command       -       command with data
+ * intel_scu_ipc_command_with_size() - Command with data
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  * @in: Input data
- * @inlen: Input length in dwords
+ * @inlen: Input length in bytes
+ * @size: Input size written to the IPC command register in whatever
+ *       units (dword, byte) the particular firmware requires. Normally
+ *       should be the same as @inlen.
  * @out: Output data
- * @outlen: Output length in dwords
+ * @outlen: Output length in bytes
  *
  * Issue a command to the SCU which involves data transfers. Do the
  * data copies under the lock but leave it for the caller to interpret.
  */
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-                         u32 *out, int outlen)
+int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
+                                       int sub, const void *in, size_t inlen,
+                                       size_t size, void *out, size_t outlen)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
+       size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
+       u32 cmdval, inbuf[4] = {};
        int i, err;
 
+       if (inbuflen > 4 || outbuflen > 4)
+               return -EINVAL;
+
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
 
-       for (i = 0; i < inlen; i++)
-               ipc_data_writel(scu, *in++, 4 * i);
+       memcpy(inbuf, in, inlen);
+       for (i = 0; i < inbuflen; i++)
+               ipc_data_writel(scu, inbuf[i], 4 * i);
 
-       ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
+       cmdval = (size << 16) | (sub << 12) | cmd;
+       ipc_command(scu, cmdval);
        err = intel_scu_ipc_check_status(scu);
 
        if (!err) {
-               for (i = 0; i < outlen; i++)
-                       *out++ = ipc_data_readl(scu, 4 * i);
+               u32 outbuf[4] = {};
+
+               for (i = 0; i < outbuflen; i++)
+                       outbuf[i] = ipc_data_readl(scu, 4 * i);
+
+               memcpy(out, outbuf, outlen);
        }
 
        mutex_unlock(&ipclock);
+       if (err)
+               dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
        return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
 
 /*
  * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
@@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void intel_scu_ipc_release(struct device *dev)
+{
+       struct intel_scu_ipc_dev *scu;
+
+       scu = container_of(dev, struct intel_scu_ipc_dev, dev);
+       if (scu->irq > 0)
+               free_irq(scu->irq, scu);
+       iounmap(scu->ipc_base);
+       release_mem_region(scu->mem.start, resource_size(&scu->mem));
+       kfree(scu);
+}
+
 /**
- *     ipc_probe       -       probe an Intel SCU IPC
- *     @pdev: the PCI device matching
- *     @id: entry in the match table
+ * __intel_scu_ipc_register() - Register SCU IPC device
+ * @parent: Parent device
+ * @scu_data: Data used to configure SCU IPC
+ * @owner: Module registering the SCU IPC device
  *
- *     Enable and install an intel SCU IPC. This appears in the PCI space
- *     but uses some hard coded addresses as well.
+ * Call this function to register SCU IPC mechanism under @parent.
+ * Returns pointer to the new SCU IPC device or ERR_PTR() in case of
+ * failure. The caller may use the returned instance if it needs to do
+ * SCU IPC calls itself.
  */
-static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+struct intel_scu_ipc_dev *
+__intel_scu_ipc_register(struct device *parent,
+                        const struct intel_scu_ipc_data *scu_data,
+                        struct module *owner)
 {
        int err;
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       struct intel_scu_ipc_dev *scu;
+       void __iomem *ipc_base;
 
-       if (scu->dev)           /* We support only one SCU */
-               return -EBUSY;
+       mutex_lock(&ipclock);
+       /* We support only one IPC */
+       if (ipcdev) {
+               err = -EBUSY;
+               goto err_unlock;
+       }
 
-       err = pcim_enable_device(pdev);
-       if (err)
-               return err;
+       scu = kzalloc(sizeof(*scu), GFP_KERNEL);
+       if (!scu) {
+               err = -ENOMEM;
+               goto err_unlock;
+       }
 
-       err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
-       if (err)
-               return err;
+       scu->owner = owner;
+       scu->dev.parent = parent;
+       scu->dev.class = &intel_scu_ipc_class;
+       scu->dev.release = intel_scu_ipc_release;
+       dev_set_name(&scu->dev, "intel_scu_ipc");
+
+       if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem),
+                               "intel_scu_ipc")) {
+               err = -EBUSY;
+               goto err_free;
+       }
 
+       ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem));
+       if (!ipc_base) {
+               err = -ENOMEM;
+               goto err_release;
+       }
+
+       scu->ipc_base = ipc_base;
+       scu->mem = scu_data->mem;
+       scu->irq = scu_data->irq;
        init_completion(&scu->cmd_complete);
 
-       scu->ipc_base = pcim_iomap_table(pdev)[0];
+       if (scu->irq > 0) {
+               err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
+               if (err)
+                       goto err_unmap;
+       }
 
-       err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
-                              scu);
-       if (err)
-               return err;
+       /*
+        * After this point intel_scu_ipc_release() takes care of
+        * releasing the SCU IPC resources once refcount drops to zero.
+        */
+       err = device_register(&scu->dev);
+       if (err) {
+               put_device(&scu->dev);
+               goto err_unlock;
+       }
 
        /* Assign device at last */
-       scu->dev = &pdev->dev;
+       ipcdev = scu;
+       mutex_unlock(&ipclock);
 
-       intel_scu_devices_create();
+       return scu;
 
-       pci_set_drvdata(pdev, scu);
-       return 0;
+err_unmap:
+       iounmap(ipc_base);
+err_release:
+       release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem));
+err_free:
+       kfree(scu);
+err_unlock:
+       mutex_unlock(&ipclock);
+
+       return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
 
-static const struct pci_device_id pci_ids[] = {
-       { PCI_VDEVICE(INTEL, 0x080e) },
-       { PCI_VDEVICE(INTEL, 0x08ea) },
-       { PCI_VDEVICE(INTEL, 0x11a0) },
-       {}
-};
+/**
+ * intel_scu_ipc_unregister() - Unregister SCU IPC
+ * @scu: SCU IPC handle
+ *
+ * This unregisters the SCU IPC device and releases the acquired
+ * resources once the refcount goes to zero.
+ */
+void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
+{
+       mutex_lock(&ipclock);
+       if (!WARN_ON(!ipcdev)) {
+               ipcdev = NULL;
+               device_unregister(&scu->dev);
+       }
+       mutex_unlock(&ipclock);
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
 
-static struct pci_driver ipc_driver = {
-       .driver = {
-               .suppress_bind_attrs = true,
-       },
-       .name = "intel_scu_ipc",
-       .id_table = pci_ids,
-       .probe = ipc_probe,
-};
-builtin_pci_driver(ipc_driver);
+static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
+{
+       struct intel_scu_ipc_devres *dr = res;
+       struct intel_scu_ipc_dev *scu = dr->scu;
+
+       intel_scu_ipc_unregister(scu);
+}
+
+/**
+ * __devm_intel_scu_ipc_register() - Register managed SCU IPC device
+ * @parent: Parent device
+ * @scu_data: Data used to configure SCU IPC
+ * @owner: Module registering the SCU IPC device
+ *
+ * Call this function to register managed SCU IPC mechanism under
+ * @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
+ * case of failure. The caller may use the returned instance if it needs
+ * to do SCU IPC calls itself.
+ */
+struct intel_scu_ipc_dev *
+__devm_intel_scu_ipc_register(struct device *parent,
+                             const struct intel_scu_ipc_data *scu_data,
+                             struct module *owner)
+{
+       struct intel_scu_ipc_devres *dr;
+       struct intel_scu_ipc_dev *scu;
+
+       dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return NULL;
+
+       scu = __intel_scu_ipc_register(parent, scu_data, owner);
+       if (IS_ERR(scu)) {
+               devres_free(dr);
+               return scu;
+       }
+
+       dr->scu = scu;
+       devres_add(parent, dr);
+
+       return scu;
+}
+EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
+
+static int __init intel_scu_ipc_init(void)
+{
+       return class_register(&intel_scu_ipc_class);
+}
+subsys_initcall(intel_scu_ipc_init);
+
+static void __exit intel_scu_ipc_exit(void)
+{
+       class_unregister(&intel_scu_ipc_class);
+}
+module_exit(intel_scu_ipc_exit);