i2c: Convert SCx200 driver from using raw PCI to platform device
Andres Salomon [Fri, 31 Dec 2010 04:27:33 +0000 (20:27 -0800)]
The SCx200 ACB driver supports ISA hardware as well as PCI.  The PCI
hardware is CS5535/CS5536 based, and the device that it grabs is handled by
the cs5535-mfd driver. This converts the SCx200 driver to use a
platform_driver rather than the previous PCI hackery.

The driver used to manually track the iface list (via linked list); now it
only does this for ISA devices.  PCI ifaces are handled through standard
driver model lists.

It's unclear what happens in case of errors in the old ISA code; rather than
pretending the code actually cares, I've dropped the (implicit) ignorance
of return values and marked it with a comment.

Signed-off-by: Andres Salomon <dilinger@queued.net>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

drivers/i2c/busses/scx200_acb.c

index 53fab51..986e5f6 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/pci.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
@@ -40,6 +41,7 @@
 
 MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
 MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
+MODULE_ALIAS("platform:cs5535-smb");
 MODULE_LICENSE("GPL");
 
 #define MAX_DEVICES 4
@@ -84,10 +86,6 @@ struct scx200_acb_iface {
        u8 *ptr;
        char needs_reset;
        unsigned len;
-
-       /* PCI device info */
-       struct pci_dev *pdev;
-       int bar;
 };
 
 /* Register Definitions */
@@ -391,7 +389,7 @@ static const struct i2c_algorithm scx200_acb_algorithm = {
 static struct scx200_acb_iface *scx200_acb_list;
 static DEFINE_MUTEX(scx200_acb_list_mutex);
 
-static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
+static __devinit int scx200_acb_probe(struct scx200_acb_iface *iface)
 {
        u8 val;
 
@@ -427,7 +425,7 @@ static __init int scx200_acb_probe(struct scx200_acb_iface *iface)
        return 0;
 }
 
-static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
+static __devinit struct scx200_acb_iface *scx200_create_iface(const char *text,
                struct device *dev, int index)
 {
        struct scx200_acb_iface *iface;
@@ -452,7 +450,7 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
        return iface;
 }
 
-static int __init scx200_acb_create(struct scx200_acb_iface *iface)
+static int __devinit scx200_acb_create(struct scx200_acb_iface *iface)
 {
        struct i2c_adapter *adapter;
        int rc;
@@ -472,183 +470,145 @@ static int __init scx200_acb_create(struct scx200_acb_iface *iface)
                return -ENODEV;
        }
 
-       mutex_lock(&scx200_acb_list_mutex);
-       iface->next = scx200_acb_list;
-       scx200_acb_list = iface;
-       mutex_unlock(&scx200_acb_list_mutex);
+       if (!adapter->dev.parent) {
+               /* If there's no dev, we're tracking (ISA) ifaces manually */
+               mutex_lock(&scx200_acb_list_mutex);
+               iface->next = scx200_acb_list;
+               scx200_acb_list = iface;
+               mutex_unlock(&scx200_acb_list_mutex);
+       }
 
        return 0;
 }
 
-static __init int scx200_create_pci(const char *text, struct pci_dev *pdev,
-               int bar)
+static struct scx200_acb_iface * __devinit scx200_create_dev(const char *text,
+               unsigned long base, int index, struct device *dev)
 {
        struct scx200_acb_iface *iface;
        int rc;
 
-       iface = scx200_create_iface(text, &pdev->dev, 0);
+       iface = scx200_create_iface(text, dev, index);
 
        if (iface == NULL)
-               return -ENOMEM;
-
-       iface->pdev = pdev;
-       iface->bar = bar;
-
-       rc = pci_enable_device_io(iface->pdev);
-       if (rc)
-               goto errout_free;
+               return NULL;
 
-       rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name);
-       if (rc) {
-               printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n",
-                               iface->bar);
+       if (!request_region(base, 8, iface->adapter.name)) {
+               printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n",
+                      base, base + 8 - 1);
                goto errout_free;
        }
 
-       iface->base = pci_resource_start(iface->pdev, iface->bar);
+       iface->base = base;
        rc = scx200_acb_create(iface);
 
        if (rc == 0)
-               return 0;
+               return iface;
 
-       pci_release_region(iface->pdev, iface->bar);
-       pci_dev_put(iface->pdev);
+       release_region(base, 8);
  errout_free:
        kfree(iface);
-       return rc;
+       return NULL;
 }
 
-static int __init scx200_create_isa(const char *text, unsigned long base,
-               int index)
+static int __devinit scx200_probe(struct platform_device *pdev)
 {
        struct scx200_acb_iface *iface;
-       int rc;
-
-       iface = scx200_create_iface(text, NULL, index);
-
-       if (iface == NULL)
-               return -ENOMEM;
+       struct resource *res;
 
-       if (!request_region(base, 8, iface->adapter.name)) {
-               printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n",
-                      base, base + 8 - 1);
-               rc = -EBUSY;
-               goto errout_free;
+       res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "can't fetch device resource info\n");
+               return -ENODEV;
        }
 
-       iface->base = base;
-       rc = scx200_acb_create(iface);
+       iface = scx200_create_dev("CS5535", res->start, 0, &pdev->dev);
+       if (!iface)
+               return -EIO;
 
-       if (rc == 0)
-               return 0;
+       dev_info(&pdev->dev, "SCx200 device '%s' registered\n",
+                       iface->adapter.name);
+       platform_set_drvdata(pdev, iface);
 
-       release_region(base, 8);
- errout_free:
-       kfree(iface);
-       return rc;
+       return 0;
 }
 
-/* Driver data is an index into the scx200_data array that indicates
- * the name and the BAR where the I/O address resource is located.  ISA
- * devices are flagged with a bar value of -1 */
-
-static const struct pci_device_id scx200_pci[] __initconst = {
-       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE),
-         .driver_data = 0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE),
-         .driver_data = 0 },
-       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA),
-         .driver_data = 1 },
-       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA),
-         .driver_data = 2 },
-       { 0, }
-};
-
-static struct {
-       const char *name;
-       int bar;
-} scx200_data[] = {
-       { "SCx200", -1 },
-       { "CS5535",  0 },
-       { "CS5536",  0 }
-};
+static void __devexit scx200_cleanup_iface(struct scx200_acb_iface *iface)
+{
+       i2c_del_adapter(&iface->adapter);
+       release_region(iface->base, 8);
+       kfree(iface);
+}
 
-static __init int scx200_scan_pci(void)
+static int __devexit scx200_remove(struct platform_device *pdev)
 {
-       int data, dev;
-       int rc = -ENODEV;
-       struct pci_dev *pdev;
+       struct scx200_acb_iface *iface;
 
-       for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) {
-               pdev = pci_get_device(scx200_pci[dev].vendor,
-                               scx200_pci[dev].device, NULL);
+       iface = platform_get_drvdata(pdev);
+       platform_set_drvdata(pdev, NULL);
+       scx200_cleanup_iface(iface);
 
-               if (pdev == NULL)
-                       continue;
+       return 0;
+}
 
-               data = scx200_pci[dev].driver_data;
+static struct platform_driver scx200_pci_drv = {
+       .driver = {
+               .name = "cs5535-smb",
+               .owner = THIS_MODULE,
+       },
+       .probe = scx200_probe,
+       .remove = __devexit_p(scx200_remove),
+};
 
-               /* if .bar is greater or equal to zero, this is a
-                * PCI device - otherwise, we assume
-                  that the ports are ISA based
-               */
+static const struct pci_device_id scx200_isa[] __initconst = {
+       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
+       { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
+       { 0, }
+};
 
-               if (scx200_data[data].bar >= 0)
-                       rc = scx200_create_pci(scx200_data[data].name, pdev,
-                                       scx200_data[data].bar);
-               else {
-                       int i;
+static __init void scx200_scan_isa(void)
+{
+       int i;
 
-                       pci_dev_put(pdev);
-                       for (i = 0; i < MAX_DEVICES; ++i) {
-                               if (base[i] == 0)
-                                       continue;
+       if (!pci_dev_present(scx200_isa))
+               return;
 
-                               rc = scx200_create_isa(scx200_data[data].name,
-                                               base[i],
-                                               i);
-                       }
-               }
+       for (i = 0; i < MAX_DEVICES; ++i) {
+               if (base[i] == 0)
+                       continue;
 
-               break;
+               /* XXX: should we care about failures? */
+               scx200_create_dev("SCx200", base[i], i, NULL);
        }
-
-       return rc;
 }
 
 static int __init scx200_acb_init(void)
 {
-       int rc;
-
        pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
 
-       rc = scx200_scan_pci();
+       /* First scan for ISA-based devices */
+       scx200_scan_isa();      /* XXX: should we care about errors? */
 
        /* If at least one bus was created, init must succeed */
        if (scx200_acb_list)
                return 0;
-       return rc;
+
+       /* No ISA devices; register the platform driver for PCI-based devices */
+       return platform_driver_register(&scx200_pci_drv);
 }
 
 static void __exit scx200_acb_cleanup(void)
 {
        struct scx200_acb_iface *iface;
 
+       platform_driver_unregister(&scx200_pci_drv);
+
        mutex_lock(&scx200_acb_list_mutex);
        while ((iface = scx200_acb_list) != NULL) {
                scx200_acb_list = iface->next;
                mutex_unlock(&scx200_acb_list_mutex);
 
-               i2c_del_adapter(&iface->adapter);
-
-               if (iface->pdev) {
-                       pci_release_region(iface->pdev, iface->bar);
-                       pci_dev_put(iface->pdev);
-               }
-               else
-                       release_region(iface->base, 8);
+               scx200_cleanup_iface(iface);
 
-               kfree(iface);
                mutex_lock(&scx200_acb_list_mutex);
        }
        mutex_unlock(&scx200_acb_list_mutex);