]>
Commit | Line | Data |
---|---|---|
d6ea3df0 SAS |
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 | ||
0f3e1d27 | 10 | struct ce4100_info { |
d6ea3df0 | 11 | struct ssp_device ssp; |
0f3e1d27 | 12 | struct platform_device *spi_pdev; |
d6ea3df0 SAS |
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 | ||
d6ea3df0 SAS |
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; | |
0f3e1d27 | 59 | struct ce4100_info *spi_info; |
d6ea3df0 | 60 | struct platform_device *pdev; |
0f3e1d27 | 61 | struct pxa2xx_spi_master spi_pdata; |
d6ea3df0 SAS |
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 | ||
0f3e1d27 | 78 | pdev = platform_device_alloc("pxa2xx-spi", dev->devfn); |
d6ea3df0 | 79 | spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); |
0f3e1d27 | 80 | if (!pdev || !spi_info ) { |
d6ea3df0 | 81 | ret = -ENOMEM; |
0f3e1d27 | 82 | goto err_nomem; |
d6ea3df0 | 83 | } |
0f3e1d27 SAS |
84 | memset(&spi_pdata, 0, sizeof(spi_pdata)); |
85 | spi_pdata.num_chipselect = dev->devfn; | |
d6ea3df0 | 86 | |
0f3e1d27 SAS |
87 | ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata)); |
88 | if (ret) | |
89 | goto err_nomem; | |
d6ea3df0 | 90 | |
0f3e1d27 | 91 | pdev->dev.parent = &dev->dev; |
d6ea3df0 SAS |
92 | #ifdef CONFIG_OF |
93 | pdev->dev.of_node = dev->dev.of_node; | |
94 | #endif | |
0f3e1d27 | 95 | ssp = &spi_info->ssp; |
d6ea3df0 SAS |
96 | ssp->phys_base = pci_resource_start(dev, 0); |
97 | ssp->mmio_base = ioremap(phys_beg, phys_len); | |
98 | if (!ssp->mmio_base) { | |
99 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | |
100 | ret = -EIO; | |
0f3e1d27 | 101 | goto err_nomem; |
d6ea3df0 SAS |
102 | } |
103 | ssp->irq = dev->irq; | |
104 | ssp->port_id = pdev->id; | |
105 | ssp->type = PXA25x_SSP; | |
106 | ||
107 | mutex_lock(&ssp_lock); | |
108 | list_add(&ssp->node, &ssp_list); | |
109 | mutex_unlock(&ssp_lock); | |
110 | ||
111 | pci_set_drvdata(dev, spi_info); | |
112 | ||
0f3e1d27 | 113 | ret = platform_device_add(pdev); |
d6ea3df0 SAS |
114 | if (ret) |
115 | goto err_dev_add; | |
116 | ||
117 | return ret; | |
118 | ||
119 | err_dev_add: | |
120 | pci_set_drvdata(dev, NULL); | |
121 | mutex_lock(&ssp_lock); | |
122 | list_del(&ssp->node); | |
123 | mutex_unlock(&ssp_lock); | |
124 | iounmap(ssp->mmio_base); | |
125 | ||
0f3e1d27 | 126 | err_nomem: |
d6ea3df0 | 127 | release_mem_region(phys_beg, phys_len); |
0f3e1d27 SAS |
128 | platform_device_put(pdev); |
129 | kfree(spi_info); | |
d6ea3df0 SAS |
130 | return ret; |
131 | } | |
132 | ||
133 | static void __devexit ce4100_spi_remove(struct pci_dev *dev) | |
134 | { | |
0f3e1d27 | 135 | struct ce4100_info *spi_info; |
d6ea3df0 SAS |
136 | struct ssp_device *ssp; |
137 | ||
138 | spi_info = pci_get_drvdata(dev); | |
d6ea3df0 | 139 | ssp = &spi_info->ssp; |
0f3e1d27 | 140 | platform_device_unregister(spi_info->spi_pdev); |
d6ea3df0 SAS |
141 | |
142 | iounmap(ssp->mmio_base); | |
143 | release_mem_region(pci_resource_start(dev, 0), | |
144 | pci_resource_len(dev, 0)); | |
145 | ||
146 | mutex_lock(&ssp_lock); | |
147 | list_del(&ssp->node); | |
148 | mutex_unlock(&ssp_lock); | |
149 | ||
150 | pci_set_drvdata(dev, NULL); | |
151 | pci_disable_device(dev); | |
152 | kfree(spi_info); | |
153 | } | |
154 | ||
155 | static struct pci_device_id ce4100_spi_devices[] __devinitdata = { | |
d6ea3df0 SAS |
156 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, |
157 | { }, | |
158 | }; | |
159 | MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); | |
160 | ||
161 | static struct pci_driver ce4100_spi_driver = { | |
162 | .name = "ce4100_spi", | |
163 | .id_table = ce4100_spi_devices, | |
164 | .probe = ce4100_spi_probe, | |
165 | .remove = __devexit_p(ce4100_spi_remove), | |
166 | }; | |
167 | ||
168 | static int __init ce4100_spi_init(void) | |
169 | { | |
170 | return pci_register_driver(&ce4100_spi_driver); | |
171 | } | |
172 | module_init(ce4100_spi_init); | |
173 | ||
174 | static void __exit ce4100_spi_exit(void) | |
175 | { | |
176 | pci_unregister_driver(&ce4100_spi_driver); | |
177 | } | |
178 | module_exit(ce4100_spi_exit); | |
179 | ||
180 | MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); | |
181 | MODULE_LICENSE("GPL v2"); | |
182 | MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); |