diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/net/caif | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/net/caif')
-rw-r--r-- | drivers/net/caif/tegra_caif_sspi.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/drivers/net/caif/tegra_caif_sspi.c b/drivers/net/caif/tegra_caif_sspi.c new file mode 100644 index 00000000000..c2c15c18f2e --- /dev/null +++ b/drivers/net/caif/tegra_caif_sspi.c | |||
@@ -0,0 +1,426 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/wait.h> | ||
24 | #include <linux/gpio.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/dma-mapping.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/spi/spi.h> | ||
29 | #include <linux/tegra_caif.h> | ||
30 | #include <mach/spi.h> | ||
31 | #include <net/caif/caif_spi.h> | ||
32 | |||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | #define SPI_CAIF_PAD_TRANSACTION_SIZE(x) \ | ||
36 | (((x) > 4) ? ((((x) + 15) / 16) * 16) : (x)) | ||
37 | |||
38 | struct sspi_struct { | ||
39 | struct cfspi_dev sdev; | ||
40 | struct cfspi_xfer *xfer; | ||
41 | }; | ||
42 | |||
43 | static struct sspi_struct slave; | ||
44 | static struct platform_device slave_device; | ||
45 | static struct spi_device *tegra_caif_spi_slave_device; | ||
46 | int tegra_caif_sspi_gpio_spi_int; | ||
47 | int tegra_caif_sspi_gpio_spi_ss; | ||
48 | int tegra_caif_sspi_gpio_reset; | ||
49 | int tegra_caif_sspi_gpio_power; | ||
50 | int tegra_caif_sspi_gpio_awr; | ||
51 | int tegra_caif_sspi_gpio_cwr; | ||
52 | |||
53 | |||
54 | static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi); | ||
55 | |||
56 | static int tegra_caif_spi_slave_remove(struct spi_device *spi) | ||
57 | { | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | #ifdef CONFIG_PM | ||
62 | static int tegra_caif_spi_slave_suspend(struct spi_device *spi | ||
63 | , pm_message_t mesg) | ||
64 | { | ||
65 | return 0; | ||
66 | } | ||
67 | #endif /* CONFIG_PM */ | ||
68 | |||
69 | #ifdef CONFIG_PM | ||
70 | static int tegra_caif_spi_slave_resume(struct spi_device *spi) | ||
71 | { | ||
72 | return 0; | ||
73 | } | ||
74 | #endif /* CONFIG_PM */ | ||
75 | |||
76 | static struct spi_driver tegra_caif_spi_slave_driver = { | ||
77 | .driver = { | ||
78 | .name = "baseband_spi_slave0.0", | ||
79 | .owner = THIS_MODULE, | ||
80 | }, | ||
81 | .probe = tegra_caif_spi_slave_probe, | ||
82 | .remove = __devexit_p(tegra_caif_spi_slave_remove), | ||
83 | #ifdef CONFIG_PM | ||
84 | .suspend = tegra_caif_spi_slave_suspend, | ||
85 | .resume = tegra_caif_spi_slave_resume, | ||
86 | #endif /* CONFIG_PM */ | ||
87 | }; | ||
88 | |||
89 | void tegra_caif_modem_power(int on) | ||
90 | { | ||
91 | static int u3xx_on; | ||
92 | int err; | ||
93 | int cnt = 0; | ||
94 | int val = 0; | ||
95 | |||
96 | if (u3xx_on == on) | ||
97 | return; | ||
98 | u3xx_on = on; | ||
99 | |||
100 | if (u3xx_on) { | ||
101 | /* turn on u3xx modem */ | ||
102 | err = gpio_request(tegra_caif_sspi_gpio_reset | ||
103 | , "caif_sspi_reset"); | ||
104 | if (err < 0) | ||
105 | goto err1; | ||
106 | |||
107 | err = gpio_request(tegra_caif_sspi_gpio_power | ||
108 | , "caif_sspi_power"); | ||
109 | if (err < 0) | ||
110 | goto err2; | ||
111 | |||
112 | err = gpio_request(tegra_caif_sspi_gpio_awr | ||
113 | , "caif_sspi_awr"); | ||
114 | if (err < 0) | ||
115 | goto err3; | ||
116 | |||
117 | err = gpio_request(tegra_caif_sspi_gpio_cwr | ||
118 | , "caif_sspi_cwr"); | ||
119 | if (err < 0) | ||
120 | goto err4; | ||
121 | |||
122 | err = gpio_direction_output(tegra_caif_sspi_gpio_reset | ||
123 | , 0 /* asserted */); | ||
124 | if (err < 0) | ||
125 | goto err5; | ||
126 | |||
127 | err = gpio_direction_output(tegra_caif_sspi_gpio_power | ||
128 | , 0 /* off */); | ||
129 | if (err < 0) | ||
130 | goto err6; | ||
131 | |||
132 | err = gpio_direction_output(tegra_caif_sspi_gpio_awr | ||
133 | , 0); | ||
134 | if (err < 0) | ||
135 | goto err7; | ||
136 | |||
137 | err = gpio_direction_input(tegra_caif_sspi_gpio_cwr); | ||
138 | if (err < 0) | ||
139 | goto err8; | ||
140 | |||
141 | gpio_set_value(tegra_caif_sspi_gpio_power, 0); | ||
142 | gpio_set_value(tegra_caif_sspi_gpio_reset, 0); | ||
143 | |||
144 | msleep(800); | ||
145 | |||
146 | /* pulse modem power on for 300 ms */ | ||
147 | gpio_set_value(tegra_caif_sspi_gpio_reset | ||
148 | , 1 /* deasserted */); | ||
149 | msleep(300); | ||
150 | gpio_set_value(tegra_caif_sspi_gpio_power, 1); | ||
151 | msleep(300); | ||
152 | gpio_set_value(tegra_caif_sspi_gpio_power, 0); | ||
153 | msleep(100); | ||
154 | |||
155 | /* set awr high */ | ||
156 | gpio_set_value(tegra_caif_sspi_gpio_awr, 1); | ||
157 | val = gpio_get_value(tegra_caif_sspi_gpio_cwr); | ||
158 | while (!val) { | ||
159 | /* wait for cwr to go high */ | ||
160 | val = gpio_get_value(tegra_caif_sspi_gpio_cwr); | ||
161 | pr_info("."); | ||
162 | msleep(100); | ||
163 | cnt++; | ||
164 | if (cnt > 200) { | ||
165 | pr_err("\nWaiting for CWR timed out - ERROR\n"); | ||
166 | break; | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | return; | ||
171 | err8: | ||
172 | err7: | ||
173 | err6: | ||
174 | err5: | ||
175 | gpio_free(tegra_caif_sspi_gpio_cwr); | ||
176 | err4: | ||
177 | gpio_free(tegra_caif_sspi_gpio_awr); | ||
178 | err3: | ||
179 | gpio_free(tegra_caif_sspi_gpio_power); | ||
180 | err2: | ||
181 | gpio_free(tegra_caif_sspi_gpio_reset); | ||
182 | err1: | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | static irqreturn_t sspi_irq(int irq, void *arg) | ||
187 | { | ||
188 | /* You only need to trigger on an edge to the active state of the | ||
189 | * SS signal. Once a edge is detected, the ss_cb() function should | ||
190 | * be called with the parameter assert set to true. It is OK | ||
191 | * (and even advised) to call the ss_cb() function in IRQ context | ||
192 | * in order not to add any delay. | ||
193 | */ | ||
194 | int val; | ||
195 | struct cfspi_dev *sdev = (struct cfspi_dev *)arg; | ||
196 | val = gpio_get_value(tegra_caif_sspi_gpio_spi_ss); | ||
197 | if (val) | ||
198 | return IRQ_HANDLED; | ||
199 | sdev->ifc->ss_cb(true, sdev->ifc); | ||
200 | return IRQ_HANDLED; | ||
201 | } | ||
202 | |||
203 | static int sspi_callback(void *arg) | ||
204 | { | ||
205 | /* for each spi_sync() call | ||
206 | * - sspi_callback() called before spi transfer | ||
207 | * - sspi_complete() called after spi transfer | ||
208 | */ | ||
209 | |||
210 | /* set master interrupt gpio pin active (tells master to | ||
211 | * start spi clock) | ||
212 | */ | ||
213 | udelay(MIN_TRANSITION_TIME_USEC); | ||
214 | gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void sspi_complete(void *context) | ||
219 | { | ||
220 | /* Normally the DMA or the SPI framework will call you back | ||
221 | * in something similar to this. The only thing you need to | ||
222 | * do is to call the xfer_done_cb() function, providing the pointer | ||
223 | * to the CAIF SPI interface. It is OK to call this function | ||
224 | * from IRQ context. | ||
225 | */ | ||
226 | |||
227 | struct cfspi_dev *sdev = (struct cfspi_dev *)context; | ||
228 | sdev->ifc->xfer_done_cb(sdev->ifc); | ||
229 | } | ||
230 | |||
231 | static void swap_byte(unsigned char *buf, unsigned int bufsiz) | ||
232 | { | ||
233 | unsigned int i; | ||
234 | unsigned char tmp; | ||
235 | for (i = 0; i < bufsiz; i += 2) { | ||
236 | tmp = buf[i]; | ||
237 | buf[i] = buf[i+1]; | ||
238 | buf[i+1] = tmp; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev) | ||
243 | { | ||
244 | /* Store transfer info. For a normal implementation you should | ||
245 | * set up your DMA here and make sure that you are ready to | ||
246 | * receive the data from the master SPI. | ||
247 | */ | ||
248 | |||
249 | struct sspi_struct *sspi = (struct sspi_struct *)dev->priv; | ||
250 | struct spi_message m; | ||
251 | struct spi_transfer t; | ||
252 | int err; | ||
253 | |||
254 | sspi->xfer = xfer; | ||
255 | |||
256 | if (!tegra_caif_spi_slave_device) | ||
257 | return -ENODEV; | ||
258 | |||
259 | err = spi_tegra_register_callback(tegra_caif_spi_slave_device, | ||
260 | sspi_callback, sspi); | ||
261 | if (err < 0) { | ||
262 | pr_err("\nspi_tegra_register_callback() failed\n"); | ||
263 | return -ENODEV; | ||
264 | } | ||
265 | memset(&t, 0, sizeof(t)); | ||
266 | t.tx_buf = xfer->va_tx; | ||
267 | swap_byte(xfer->va_tx, xfer->tx_dma_len); | ||
268 | t.rx_buf = xfer->va_rx; | ||
269 | t.len = max(xfer->tx_dma_len, xfer->rx_dma_len); | ||
270 | t.len = SPI_CAIF_PAD_TRANSACTION_SIZE(t.len); | ||
271 | t.bits_per_word = 16; | ||
272 | /* SPI controller clock should be 4 times the spi_clk */ | ||
273 | t.speed_hz = (SPI_MASTER_CLK_MHZ * 4 * 1000000); | ||
274 | spi_message_init(&m); | ||
275 | spi_message_add_tail(&t, &m); | ||
276 | |||
277 | dmb(); | ||
278 | err = spi_sync(tegra_caif_spi_slave_device, &m); | ||
279 | dmb(); | ||
280 | swap_byte(xfer->va_tx, xfer->tx_dma_len); | ||
281 | swap_byte(xfer->va_rx, xfer->rx_dma_len); | ||
282 | sspi_complete(&sspi->sdev); | ||
283 | if (err < 0) { | ||
284 | pr_err("spi_init_xfer - spi_sync() err %d\n", err); | ||
285 | return err; | ||
286 | } | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev) | ||
291 | { | ||
292 | /* If xfer is true then you should assert the SPI_INT to indicate to | ||
293 | * the master that you are ready to recieve the data from the master | ||
294 | * SPI. If xfer is false then you should de-assert SPI_INT to indicate | ||
295 | * that the transfer is done. | ||
296 | */ | ||
297 | if (xfer) | ||
298 | gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1); | ||
299 | else | ||
300 | gpio_set_value(tegra_caif_sspi_gpio_spi_int, 0); | ||
301 | } | ||
302 | |||
303 | static void sspi_release(struct device *dev) | ||
304 | { | ||
305 | /* | ||
306 | * Here you should release your SPI device resources. | ||
307 | */ | ||
308 | } | ||
309 | |||
310 | static int __init sspi_init(void) | ||
311 | { | ||
312 | /* Here you should initialize your SPI device by providing the | ||
313 | * necessary functions, clock speed, name and private data. Once | ||
314 | * done, you can register your device with the | ||
315 | * platform_device_register() function. This function will return | ||
316 | * with the CAIF SPI interface initialized. This is probably also | ||
317 | * the place where you should set up your GPIOs, interrupts and SPI | ||
318 | * resources. | ||
319 | */ | ||
320 | |||
321 | int res = 0; | ||
322 | |||
323 | /* Register Tegra SPI protocol driver. */ | ||
324 | res = spi_register_driver(&tegra_caif_spi_slave_driver); | ||
325 | if (res < 0) | ||
326 | return res; | ||
327 | |||
328 | /* Initialize slave device. */ | ||
329 | slave.sdev.init_xfer = sspi_init_xfer; | ||
330 | slave.sdev.sig_xfer = sspi_sig_xfer; | ||
331 | slave.sdev.clk_mhz = SPI_MASTER_CLK_MHZ; | ||
332 | slave.sdev.priv = &slave; | ||
333 | slave.sdev.name = "spi_sspi"; | ||
334 | slave_device.dev.release = sspi_release; | ||
335 | |||
336 | /* Initialize platform device. */ | ||
337 | slave_device.name = "cfspi_sspi"; | ||
338 | slave_device.dev.platform_data = &slave.sdev; | ||
339 | |||
340 | /* Register platform device. */ | ||
341 | res = platform_device_register(&slave_device); | ||
342 | if (res) | ||
343 | return -ENODEV; | ||
344 | |||
345 | return res; | ||
346 | } | ||
347 | |||
348 | static void __exit sspi_exit(void) | ||
349 | { | ||
350 | /* Delete platfrom device. */ | ||
351 | platform_device_del(&slave_device); | ||
352 | |||
353 | /* Free Tegra SPI protocol driver. */ | ||
354 | spi_unregister_driver(&tegra_caif_spi_slave_driver); | ||
355 | |||
356 | /* Free Tegra GPIO interrupts. */ | ||
357 | disable_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss)); | ||
358 | free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device); | ||
359 | |||
360 | /* Free Tegra GPIOs. */ | ||
361 | gpio_free(tegra_caif_sspi_gpio_spi_ss); | ||
362 | gpio_free(tegra_caif_sspi_gpio_spi_int); | ||
363 | } | ||
364 | |||
365 | static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi) | ||
366 | { | ||
367 | struct tegra_caif_platform_data *pdata; | ||
368 | int res; | ||
369 | |||
370 | if (!spi) | ||
371 | return -ENODEV; | ||
372 | |||
373 | pdata = spi->dev.platform_data; | ||
374 | if (!pdata) | ||
375 | return -ENODEV; | ||
376 | |||
377 | tegra_caif_sspi_gpio_spi_int = pdata->spi_int; | ||
378 | tegra_caif_sspi_gpio_spi_ss = pdata->spi_ss; | ||
379 | tegra_caif_sspi_gpio_reset = pdata->reset; | ||
380 | tegra_caif_sspi_gpio_power = pdata->power; | ||
381 | tegra_caif_sspi_gpio_awr = pdata->awr; | ||
382 | tegra_caif_sspi_gpio_cwr = pdata->cwr; | ||
383 | |||
384 | tegra_caif_spi_slave_device = spi; | ||
385 | |||
386 | /* Initialize Tegra GPIOs. */ | ||
387 | res = gpio_request(tegra_caif_sspi_gpio_spi_int, "caif_sspi_spi_int"); | ||
388 | if (res < 0) | ||
389 | goto err1; | ||
390 | |||
391 | res = gpio_request(tegra_caif_sspi_gpio_spi_ss, "caif_sspi_ss"); | ||
392 | if (res < 0) | ||
393 | goto err2; | ||
394 | |||
395 | res = gpio_direction_output(tegra_caif_sspi_gpio_spi_int, 0); | ||
396 | if (res < 0) | ||
397 | goto err3; | ||
398 | |||
399 | res = gpio_direction_input(tegra_caif_sspi_gpio_spi_ss); | ||
400 | if (res < 0) | ||
401 | goto err4; | ||
402 | |||
403 | tegra_caif_modem_power(1); | ||
404 | msleep(2000); | ||
405 | |||
406 | /* Initialize Tegra GPIO interrupts. */ | ||
407 | res = request_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), | ||
408 | sspi_irq, IRQF_TRIGGER_FALLING, "caif_sspi_ss_irq", | ||
409 | &slave.sdev); | ||
410 | if (res < 0) | ||
411 | goto err5; | ||
412 | |||
413 | return 0; | ||
414 | err5: | ||
415 | free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device); | ||
416 | err4: | ||
417 | err3: | ||
418 | gpio_free(tegra_caif_sspi_gpio_spi_ss); | ||
419 | err2: | ||
420 | gpio_free(tegra_caif_sspi_gpio_spi_int); | ||
421 | err1: | ||
422 | return res; | ||
423 | } | ||
424 | |||
425 | module_init(sspi_init); | ||
426 | module_exit(sspi_exit); | ||