linux/drivers/i2c/busses/i2c-pxa-pci.c
<<
>>
Prefs
   1/*
   2 * The CE4100's I2C device is more or less the same one as found on PXA.
   3 * It does not support slave mode, the register slightly moved. This PCI
   4 * device provides three bars, every contains a single I2C controller.
   5 */
   6#include <linux/module.h>
   7#include <linux/pci.h>
   8#include <linux/platform_device.h>
   9#include <linux/i2c/pxa-i2c.h>
  10#include <linux/of.h>
  11#include <linux/of_device.h>
  12#include <linux/of_address.h>
  13
  14#define CE4100_PCI_I2C_DEVS     3
  15
  16struct ce4100_devices {
  17        struct platform_device *pdev[CE4100_PCI_I2C_DEVS];
  18};
  19
  20static struct platform_device *add_i2c_device(struct pci_dev *dev, int bar)
  21{
  22        struct platform_device *pdev;
  23        struct i2c_pxa_platform_data pdata;
  24        struct resource res[2];
  25        struct device_node *child;
  26        static int devnum;
  27        int ret;
  28
  29        memset(&pdata, 0, sizeof(struct i2c_pxa_platform_data));
  30        memset(&res, 0, sizeof(res));
  31
  32        res[0].flags = IORESOURCE_MEM;
  33        res[0].start = pci_resource_start(dev, bar);
  34        res[0].end = pci_resource_end(dev, bar);
  35
  36        res[1].flags = IORESOURCE_IRQ;
  37        res[1].start = dev->irq;
  38        res[1].end = dev->irq;
  39
  40        for_each_child_of_node(dev->dev.of_node, child) {
  41                const void *prop;
  42                struct resource r;
  43                int ret;
  44
  45                ret = of_address_to_resource(child, 0, &r);
  46                if (ret < 0)
  47                        continue;
  48                if (r.start != res[0].start)
  49                        continue;
  50                if (r.end != res[0].end)
  51                        continue;
  52                if (r.flags != res[0].flags)
  53                        continue;
  54
  55                prop = of_get_property(child, "fast-mode", NULL);
  56                if (prop)
  57                        pdata.fast_mode = 1;
  58
  59                break;
  60        }
  61
  62        if (!child) {
  63                dev_err(&dev->dev, "failed to match a DT node for bar %d.\n",
  64                                bar);
  65                ret = -EINVAL;
  66                goto out;
  67        }
  68
  69        pdev = platform_device_alloc("ce4100-i2c", devnum);
  70        if (!pdev) {
  71                of_node_put(child);
  72                ret = -ENOMEM;
  73                goto out;
  74        }
  75        pdev->dev.parent = &dev->dev;
  76        pdev->dev.of_node = child;
  77
  78        ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
  79        if (ret)
  80                goto err;
  81
  82        ret = platform_device_add_data(pdev, &pdata, sizeof(pdata));
  83        if (ret)
  84                goto err;
  85
  86        ret = platform_device_add(pdev);
  87        if (ret)
  88                goto err;
  89        devnum++;
  90        return pdev;
  91err:
  92        platform_device_put(pdev);
  93out:
  94        return ERR_PTR(ret);
  95}
  96
  97static int ce4100_i2c_probe(struct pci_dev *dev,
  98                const struct pci_device_id *ent)
  99{
 100        int ret;
 101        int i;
 102        struct ce4100_devices *sds;
 103
 104        ret = pci_enable_device_mem(dev);
 105        if (ret)
 106                return ret;
 107
 108        if (!dev->dev.of_node) {
 109                dev_err(&dev->dev, "Missing device tree node.\n");
 110                return -EINVAL;
 111        }
 112        sds = kzalloc(sizeof(*sds), GFP_KERNEL);
 113        if (!sds) {
 114                ret = -ENOMEM;
 115                goto err_mem;
 116        }
 117
 118        for (i = 0; i < ARRAY_SIZE(sds->pdev); i++) {
 119                sds->pdev[i] = add_i2c_device(dev, i);
 120                if (IS_ERR(sds->pdev[i])) {
 121                        ret = PTR_ERR(sds->pdev[i]);
 122                        while (--i >= 0)
 123                                platform_device_unregister(sds->pdev[i]);
 124                        goto err_dev_add;
 125                }
 126        }
 127        pci_set_drvdata(dev, sds);
 128        return 0;
 129
 130err_dev_add:
 131        kfree(sds);
 132err_mem:
 133        pci_disable_device(dev);
 134        return ret;
 135}
 136
 137static void ce4100_i2c_remove(struct pci_dev *dev)
 138{
 139        struct ce4100_devices *sds;
 140        unsigned int i;
 141
 142        sds = pci_get_drvdata(dev);
 143
 144        for (i = 0; i < ARRAY_SIZE(sds->pdev); i++)
 145                platform_device_unregister(sds->pdev[i]);
 146
 147        pci_disable_device(dev);
 148        kfree(sds);
 149}
 150
 151static const struct pci_device_id ce4100_i2c_devices[] = {
 152        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
 153        { },
 154};
 155MODULE_DEVICE_TABLE(pci, ce4100_i2c_devices);
 156
 157static struct pci_driver ce4100_i2c_driver = {
 158        .name           = "ce4100_i2c",
 159        .id_table       = ce4100_i2c_devices,
 160        .probe          = ce4100_i2c_probe,
 161        .remove         = ce4100_i2c_remove,
 162};
 163
 164module_pci_driver(ce4100_i2c_driver);
 165
 166MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver");
 167MODULE_LICENSE("GPL v2");
 168MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
 169