]>
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 | 92 | pdev->dev.of_node = dev->dev.of_node; |
0f3e1d27 | 93 | ssp = &spi_info->ssp; |
d6ea3df0 SAS |
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; | |
0f3e1d27 | 99 | goto err_nomem; |
d6ea3df0 SAS |
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 | ||
0f3e1d27 | 111 | ret = platform_device_add(pdev); |
d6ea3df0 SAS |
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 | ||
0f3e1d27 | 124 | err_nomem: |
d6ea3df0 | 125 | release_mem_region(phys_beg, phys_len); |
0f3e1d27 SAS |
126 | platform_device_put(pdev); |
127 | kfree(spi_info); | |
d6ea3df0 SAS |
128 | return ret; |
129 | } | |
130 | ||
131 | static void __devexit ce4100_spi_remove(struct pci_dev *dev) | |
132 | { | |
0f3e1d27 | 133 | struct ce4100_info *spi_info; |
d6ea3df0 SAS |
134 | struct ssp_device *ssp; |
135 | ||
136 | spi_info = pci_get_drvdata(dev); | |
d6ea3df0 | 137 | ssp = &spi_info->ssp; |
0f3e1d27 | 138 | platform_device_unregister(spi_info->spi_pdev); |
d6ea3df0 SAS |
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 = { | |
d6ea3df0 SAS |
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>"); |