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