diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2010-11-24 04:17:14 -0500 |
---|---|---|
committer | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2010-12-01 07:48:30 -0500 |
commit | d6ea3df0d470fb9260db93883f97764cf9f0e562 (patch) | |
tree | 277838ea93d6dc0c7d2a05be757dfa1b8bedb619 /drivers/spi/pxa2xx_spi_pci.c | |
parent | 8348c259dd6a6019a8fa01b0a3443409480f7b9d (diff) |
spi/pxa2xx: Add CE4100 support
Sodaville's SPI controller is very much the same as in PXA25x. The
difference:
- The RX/TX FIFO is only 4 words deep instead of 16
- No DMA support
- The SPI controller offers a CS functionality
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
Diffstat (limited to 'drivers/spi/pxa2xx_spi_pci.c')
-rw-r--r-- | drivers/spi/pxa2xx_spi_pci.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c new file mode 100644 index 000000000000..351d8a375b57 --- /dev/null +++ b/drivers/spi/pxa2xx_spi_pci.c | |||
@@ -0,0 +1,201 @@ | |||
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>"); | ||