#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;
+       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)
 /* 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,
+};
+
 /*
  * Send ipc command
  * Command Register (Write Only):
                usleep_range(50, 100);
        } while (time_before(jiffies, end));
 
-       dev_err(scu->dev, "IPC timed out");
+       dev_err(&scu->dev, "IPC timed out");
        return -ETIMEDOUT;
 }
 
        int status;
 
        if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
-               dev_err(scu->dev, "IPC timed out\n");
+               dev_err(&scu->dev, "IPC timed out\n");
                return -ETIMEDOUT;
        }
 
 
 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)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       struct intel_scu_ipc_dev *scu;
        int nc;
        u32 offset = 0;
        int err;
        memset(cbuf, 0, sizeof(cbuf));
 
        mutex_lock(&ipclock);
-
-       if (scu->dev == NULL) {
+       if (!ipcdev) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
+       scu = ipcdev;
 
        for (nc = 0; nc < count; nc++, offset += 2) {
                cbuf[offset] = addr[nc];
  */
 int intel_scu_ipc_simple_command(int cmd, int sub)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       struct intel_scu_ipc_dev *scu;
        int err;
 
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!ipcdev) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
+       scu = ipcdev;
        ipc_command(scu, sub << 12 | cmd);
        err = intel_scu_ipc_check_status(scu);
        mutex_unlock(&ipclock);
 int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
                          u32 *out, int outlen)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       struct intel_scu_ipc_dev *scu;
        int i, err;
 
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!ipcdev) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
+       scu = ipcdev;
 
        for (i = 0; i < inlen; i++)
                ipc_data_writel(scu, *in++, 4 * i);
        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
  *
- *     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.
  */
-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)
 {
        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->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) },
-       {}
-};
+static int __init intel_scu_ipc_init(void)
+{
+       return class_register(&intel_scu_ipc_class);
+}
+subsys_initcall(intel_scu_ipc_init);
 
-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 __exit intel_scu_ipc_exit(void)
+{
+       class_unregister(&intel_scu_ipc_class);
+}
+module_exit(intel_scu_ipc_exit);
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI driver for the Intel SCU.
+ *
+ * Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
+ * Authors: Sreedhara DS (sreedhara.ds@intel.com)
+ *         Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/intel-mid.h>
+#include <asm/intel_scu_ipc.h>
+
+static int intel_scu_pci_probe(struct pci_dev *pdev,
+                              const struct pci_device_id *id)
+{
+       struct intel_scu_ipc_data scu_data = {};
+       struct intel_scu_ipc_dev *scu;
+       int ret;
+
+       ret = pcim_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       scu_data.mem = pdev->resource[0];
+       scu_data.irq = pdev->irq;
+
+       scu = intel_scu_ipc_register(&pdev->dev, &scu_data);
+       if (IS_ERR(scu))
+               return PTR_ERR(scu);
+
+       intel_scu_devices_create();
+       return 0;
+}
+
+static const struct pci_device_id pci_ids[] = {
+       { PCI_VDEVICE(INTEL, 0x080e) },
+       { PCI_VDEVICE(INTEL, 0x08ea) },
+       { PCI_VDEVICE(INTEL, 0x11a0) },
+       {}
+};
+
+static struct pci_driver intel_scu_pci_driver = {
+       .driver = {
+               .suppress_bind_attrs = true,
+       },
+       .name = "intel_scu",
+       .id_table = pci_ids,
+       .probe = intel_scu_pci_probe,
+};
+
+builtin_pci_driver(intel_scu_pci_driver);