]>
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 | ||
10 | struct awesome_struct { | |
11 | struct ssp_device ssp; | |
12 | struct platform_device spi_pdev; | |
13 | struct pxa2xx_spi_master spi_pdata; | |
14 | }; | |
15 | ||
16 | static DEFINE_MUTEX(ssp_lock); | |
17 | static LIST_HEAD(ssp_list); | |
18 | ||
19 | struct ssp_device *pxa_ssp_request(int port, const char *label) | |
20 | { | |
21 | struct ssp_device *ssp = NULL; | |
22 | ||
23 | mutex_lock(&ssp_lock); | |
24 | ||
25 | list_for_each_entry(ssp, &ssp_list, node) { | |
26 | if (ssp->port_id == port && ssp->use_count == 0) { | |
27 | ssp->use_count++; | |
28 | ssp->label = label; | |
29 | break; | |
30 | } | |
31 | } | |
32 | ||
33 | mutex_unlock(&ssp_lock); | |
34 | ||
35 | if (&ssp->node == &ssp_list) | |
36 | return NULL; | |
37 | ||
38 | return ssp; | |
39 | } | |
40 | EXPORT_SYMBOL_GPL(pxa_ssp_request); | |
41 | ||
42 | void pxa_ssp_free(struct ssp_device *ssp) | |
43 | { | |
44 | mutex_lock(&ssp_lock); | |
45 | if (ssp->use_count) { | |
46 | ssp->use_count--; | |
47 | ssp->label = NULL; | |
48 | } else | |
49 | dev_err(&ssp->pdev->dev, "device already free\n"); | |
50 | mutex_unlock(&ssp_lock); | |
51 | } | |
52 | EXPORT_SYMBOL_GPL(pxa_ssp_free); | |
53 | ||
54 | static void plat_dev_release(struct device *dev) | |
55 | { | |
56 | struct awesome_struct *as = container_of(dev, | |
57 | struct awesome_struct, spi_pdev.dev); | |
58 | ||
59 | of_device_node_put(&as->spi_pdev.dev); | |
60 | } | |
61 | ||
62 | static int __devinit ce4100_spi_probe(struct pci_dev *dev, | |
63 | const struct pci_device_id *ent) | |
64 | { | |
65 | int ret; | |
66 | resource_size_t phys_beg; | |
67 | resource_size_t phys_len; | |
68 | struct awesome_struct *spi_info; | |
69 | struct platform_device *pdev; | |
70 | struct pxa2xx_spi_master *spi_pdata; | |
71 | struct ssp_device *ssp; | |
72 | ||
73 | ret = pci_enable_device(dev); | |
74 | if (ret) | |
75 | return ret; | |
76 | ||
77 | phys_beg = pci_resource_start(dev, 0); | |
78 | phys_len = pci_resource_len(dev, 0); | |
79 | ||
80 | if (!request_mem_region(phys_beg, phys_len, | |
81 | "CE4100 SPI")) { | |
82 | dev_err(&dev->dev, "Can't request register space.\n"); | |
83 | ret = -EBUSY; | |
84 | return ret; | |
85 | } | |
86 | ||
87 | spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); | |
88 | if (!spi_info) { | |
89 | ret = -ENOMEM; | |
90 | goto err_kz; | |
91 | } | |
92 | ssp = &spi_info->ssp; | |
93 | pdev = &spi_info->spi_pdev; | |
94 | spi_pdata = &spi_info->spi_pdata; | |
95 | ||
96 | pdev->name = "pxa2xx-spi"; | |
97 | pdev->id = dev->devfn; | |
98 | pdev->dev.parent = &dev->dev; | |
99 | pdev->dev.platform_data = &spi_info->spi_pdata; | |
100 | ||
101 | #ifdef CONFIG_OF | |
102 | pdev->dev.of_node = dev->dev.of_node; | |
103 | #endif | |
104 | pdev->dev.release = plat_dev_release; | |
105 | ||
106 | spi_pdata->num_chipselect = dev->devfn; | |
107 | ||
108 | ssp->phys_base = pci_resource_start(dev, 0); | |
109 | ssp->mmio_base = ioremap(phys_beg, phys_len); | |
110 | if (!ssp->mmio_base) { | |
111 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | |
112 | ret = -EIO; | |
113 | goto err_remap; | |
114 | } | |
115 | ssp->irq = dev->irq; | |
116 | ssp->port_id = pdev->id; | |
117 | ssp->type = PXA25x_SSP; | |
118 | ||
119 | mutex_lock(&ssp_lock); | |
120 | list_add(&ssp->node, &ssp_list); | |
121 | mutex_unlock(&ssp_lock); | |
122 | ||
123 | pci_set_drvdata(dev, spi_info); | |
124 | ||
125 | ret = platform_device_register(pdev); | |
126 | if (ret) | |
127 | goto err_dev_add; | |
128 | ||
129 | return ret; | |
130 | ||
131 | err_dev_add: | |
132 | pci_set_drvdata(dev, NULL); | |
133 | mutex_lock(&ssp_lock); | |
134 | list_del(&ssp->node); | |
135 | mutex_unlock(&ssp_lock); | |
136 | iounmap(ssp->mmio_base); | |
137 | ||
138 | err_remap: | |
139 | kfree(spi_info); | |
140 | ||
141 | err_kz: | |
142 | release_mem_region(phys_beg, phys_len); | |
143 | ||
144 | return ret; | |
145 | } | |
146 | ||
147 | static void __devexit ce4100_spi_remove(struct pci_dev *dev) | |
148 | { | |
149 | struct awesome_struct *spi_info; | |
150 | struct platform_device *pdev; | |
151 | struct ssp_device *ssp; | |
152 | ||
153 | spi_info = pci_get_drvdata(dev); | |
154 | ||
155 | ssp = &spi_info->ssp; | |
156 | pdev = &spi_info->spi_pdev; | |
157 | ||
158 | platform_device_unregister(pdev); | |
159 | ||
160 | iounmap(ssp->mmio_base); | |
161 | release_mem_region(pci_resource_start(dev, 0), | |
162 | pci_resource_len(dev, 0)); | |
163 | ||
164 | mutex_lock(&ssp_lock); | |
165 | list_del(&ssp->node); | |
166 | mutex_unlock(&ssp_lock); | |
167 | ||
168 | pci_set_drvdata(dev, NULL); | |
169 | pci_disable_device(dev); | |
170 | kfree(spi_info); | |
171 | } | |
172 | ||
173 | static struct pci_device_id ce4100_spi_devices[] __devinitdata = { | |
174 | ||
175 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, | |
176 | { }, | |
177 | }; | |
178 | MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); | |
179 | ||
180 | static struct pci_driver ce4100_spi_driver = { | |
181 | .name = "ce4100_spi", | |
182 | .id_table = ce4100_spi_devices, | |
183 | .probe = ce4100_spi_probe, | |
184 | .remove = __devexit_p(ce4100_spi_remove), | |
185 | }; | |
186 | ||
187 | static int __init ce4100_spi_init(void) | |
188 | { | |
189 | return pci_register_driver(&ce4100_spi_driver); | |
190 | } | |
191 | module_init(ce4100_spi_init); | |
192 | ||
193 | static void __exit ce4100_spi_exit(void) | |
194 | { | |
195 | pci_unregister_driver(&ce4100_spi_driver); | |
196 | } | |
197 | module_exit(ce4100_spi_exit); | |
198 | ||
199 | MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); | |
200 | MODULE_LICENSE("GPL v2"); | |
201 | MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>"); |