diff options
Diffstat (limited to 'drivers/spi/spi-pxa2xx-pci.c')
-rw-r--r-- | drivers/spi/spi-pxa2xx-pci.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c new file mode 100644 index 000000000000..378e504f89eb --- /dev/null +++ b/drivers/spi/spi-pxa2xx-pci.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * CE4100's SPI device is more or less the same one as found on PXA | ||
3 | * | ||
4 | */ | ||
5 | #include <linux/pci.h> | ||
6 | #include <linux/platform_device.h> | ||
7 | #include <linux/of_device.h> | ||
8 | #include <linux/spi/pxa2xx_spi.h> | ||
9 | |||
10 | struct ce4100_info { | ||
11 | struct ssp_device ssp; | ||
12 | struct platform_device *spi_pdev; | ||
13 | }; | ||
14 | |||
15 | static DEFINE_MUTEX(ssp_lock); | ||
16 | static LIST_HEAD(ssp_list); | ||
17 | |||
18 | struct ssp_device *pxa_ssp_request(int port, const char *label) | ||
19 | { | ||
20 | struct ssp_device *ssp = NULL; | ||
21 | |||
22 | mutex_lock(&ssp_lock); | ||
23 | |||
24 | list_for_each_entry(ssp, &ssp_list, node) { | ||
25 | if (ssp->port_id == port && ssp->use_count == 0) { | ||
26 | ssp->use_count++; | ||
27 | ssp->label = label; | ||
28 | break; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | mutex_unlock(&ssp_lock); | ||
33 | |||
34 | if (&ssp->node == &ssp_list) | ||
35 | return NULL; | ||
36 | |||
37 | return ssp; | ||
38 | } | ||
39 | EXPORT_SYMBOL_GPL(pxa_ssp_request); | ||
40 | |||
41 | void pxa_ssp_free(struct ssp_device *ssp) | ||
42 | { | ||
43 | mutex_lock(&ssp_lock); | ||
44 | if (ssp->use_count) { | ||
45 | ssp->use_count--; | ||
46 | ssp->label = NULL; | ||
47 | } else | ||
48 | dev_err(&ssp->pdev->dev, "device already free\n"); | ||
49 | mutex_unlock(&ssp_lock); | ||
50 | } | ||
51 | EXPORT_SYMBOL_GPL(pxa_ssp_free); | ||
52 | |||
53 | static int __devinit ce4100_spi_probe(struct pci_dev *dev, | ||
54 | const struct pci_device_id *ent) | ||
55 | { | ||
56 | int ret; | ||
57 | resource_size_t phys_beg; | ||
58 | resource_size_t phys_len; | ||
59 | struct ce4100_info *spi_info; | ||
60 | struct platform_device *pdev; | ||
61 | struct pxa2xx_spi_master spi_pdata; | ||
62 | struct ssp_device *ssp; | ||
63 | |||
64 | ret = pci_enable_device(dev); | ||
65 | if (ret) | ||
66 | return ret; | ||
67 | |||
68 | phys_beg = pci_resource_start(dev, 0); | ||
69 | phys_len = pci_resource_len(dev, 0); | ||
70 | |||
71 | if (!request_mem_region(phys_beg, phys_len, | ||
72 | "CE4100 SPI")) { | ||
73 | dev_err(&dev->dev, "Can't request register space.\n"); | ||
74 | ret = -EBUSY; | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | pdev = platform_device_alloc("pxa2xx-spi", dev->devfn); | ||
79 | spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); | ||
80 | if (!pdev || !spi_info ) { | ||
81 | ret = -ENOMEM; | ||
82 | goto err_nomem; | ||
83 | } | ||
84 | memset(&spi_pdata, 0, sizeof(spi_pdata)); | ||
85 | spi_pdata.num_chipselect = dev->devfn; | ||
86 | |||
87 | ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata)); | ||
88 | if (ret) | ||
89 | goto err_nomem; | ||
90 | |||
91 | pdev->dev.parent = &dev->dev; | ||
92 | pdev->dev.of_node = dev->dev.of_node; | ||
93 | ssp = &spi_info->ssp; | ||
94 | ssp->phys_base = pci_resource_start(dev, 0); | ||
95 | ssp->mmio_base = ioremap(phys_beg, phys_len); | ||
96 | if (!ssp->mmio_base) { | ||
97 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | ||
98 | ret = -EIO; | ||
99 | goto err_nomem; | ||
100 | } | ||
101 | ssp->irq = dev->irq; | ||
102 | ssp->port_id = pdev->id; | ||
103 | ssp->type = PXA25x_SSP; | ||
104 | |||
105 | mutex_lock(&ssp_lock); | ||
106 | list_add(&ssp->node, &ssp_list); | ||
107 | mutex_unlock(&ssp_lock); | ||
108 | |||
109 | pci_set_drvdata(dev, spi_info); | ||
110 | |||
111 | ret = platform_device_add(pdev); | ||
112 | if (ret) | ||
113 | goto err_dev_add; | ||
114 | |||
115 | return ret; | ||
116 | |||
117 | err_dev_add: | ||
118 | pci_set_drvdata(dev, NULL); | ||
119 | mutex_lock(&ssp_lock); | ||
120 | list_del(&ssp->node); | ||
121 | mutex_unlock(&ssp_lock); | ||
122 | iounmap(ssp->mmio_base); | ||
123 | |||
124 | err_nomem: | ||
125 | release_mem_region(phys_beg, phys_len); | ||
126 | platform_device_put(pdev); | ||
127 | kfree(spi_info); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static void __devexit ce4100_spi_remove(struct pci_dev *dev) | ||
132 | { | ||
133 | struct ce4100_info *spi_info; | ||
134 | struct ssp_device *ssp; | ||
135 | |||
136 | spi_info = pci_get_drvdata(dev); | ||
137 | ssp = &spi_info->ssp; | ||
138 | platform_device_unregister(spi_info->spi_pdev); | ||
139 | |||
140 | iounmap(ssp->mmio_base); | ||
141 | release_mem_region(pci_resource_start(dev, 0), | ||
142 | pci_resource_len(dev, 0)); | ||
143 | |||
144 | mutex_lock(&ssp_lock); | ||
145 | list_del(&ssp->node); | ||
146 | mutex_unlock(&ssp_lock); | ||
147 | |||
148 | pci_set_drvdata(dev, NULL); | ||
149 | pci_disable_device(dev); | ||
150 | kfree(spi_info); | ||
151 | } | ||
152 | |||
153 | static struct pci_device_id ce4100_spi_devices[] __devinitdata = { | ||
154 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, | ||
155 | { }, | ||
156 | }; | ||
157 | MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); | ||
158 | |||
159 | static struct pci_driver ce4100_spi_driver = { | ||
160 | .name = "ce4100_spi", | ||
161 | .id_table = ce4100_spi_devices, | ||
162 | .probe = ce4100_spi_probe, | ||
163 | .remove = __devexit_p(ce4100_spi_remove), | ||
164 | }; | ||
165 | |||
166 | static int __init ce4100_spi_init(void) | ||
167 | { | ||
168 | return pci_register_driver(&ce4100_spi_driver); | ||
169 | } | ||
170 | module_init(ce4100_spi_init); | ||
171 | |||
172 | static void __exit ce4100_spi_exit(void) | ||
173 | { | ||
174 | pci_unregister_driver(&ce4100_spi_driver); | ||
175 | } | ||
176 | module_exit(ce4100_spi_exit); | ||
177 | |||
178 | MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); | ||
179 | MODULE_LICENSE("GPL v2"); | ||
180 | MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); | ||