OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /******************************************************************************* |
2 | * |
||
3 | * CNS3XXX SPI controller driver (master mode only) |
||
4 | * |
||
5 | * Copyright (c) 2008 Cavium Networks |
||
6 | * Copyright 2011 Gateworks Corporation |
||
7 | * Chris Lang <clang@gateworks.com> |
||
8 | * |
||
9 | * This file is free software; you can redistribute it and/or modify |
||
10 | * it under the terms of the GNU General Public License, Version 2, as |
||
11 | * published by the Free Software Foundation. |
||
12 | * |
||
13 | * This file is distributed in the hope that it will be useful, |
||
14 | * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or |
||
16 | * NONINFRINGEMENT. See the GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with this file; if not, write to the Free Software |
||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or |
||
21 | * visit http://www.gnu.org/licenses/. |
||
22 | * |
||
23 | * This file may also be available under a different license from Cavium. |
||
24 | * Contact Cavium Networks for more information |
||
25 | * |
||
26 | ******************************************************************************/ |
||
27 | |||
28 | #include <linux/init.h> |
||
29 | #include <linux/spinlock.h> |
||
30 | #include <linux/workqueue.h> |
||
31 | #include <linux/interrupt.h> |
||
32 | #include <linux/delay.h> |
||
33 | #include <linux/errno.h> |
||
34 | #include <linux/err.h> |
||
35 | #include <linux/clk.h> |
||
36 | #include <linux/platform_device.h> |
||
37 | |||
38 | #include <linux/spi/spi.h> |
||
39 | #include <linux/spi/spi_bitbang.h> |
||
40 | #include <linux/mtd/partitions.h> |
||
41 | #include <linux/dma-mapping.h> |
||
42 | #include <linux/slab.h> |
||
43 | |||
44 | #include <asm/io.h> |
||
45 | #include <asm/memory.h> |
||
46 | #include <asm/dma.h> |
||
47 | #include <asm/delay.h> |
||
48 | #include <linux/module.h> |
||
49 | |||
50 | /* |
||
51 | * define access macros |
||
52 | */ |
||
53 | #define SPI_MEM_MAP_VALUE(reg_offset) (*((u32 volatile *)(hw->base + reg_offset))) |
||
54 | |||
55 | #define SPI_CONFIGURATION_REG SPI_MEM_MAP_VALUE(0x00) |
||
56 | #define SPI_SERVICE_STATUS_REG SPI_MEM_MAP_VALUE(0x04) |
||
57 | #define SPI_BIT_RATE_CONTROL_REG SPI_MEM_MAP_VALUE(0x08) |
||
58 | #define SPI_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x0C) |
||
59 | #define SPI_TRANSMIT_BUFFER_REG SPI_MEM_MAP_VALUE(0x10) |
||
60 | #define SPI_RECEIVE_CONTROL_REG SPI_MEM_MAP_VALUE(0x14) |
||
61 | #define SPI_RECEIVE_BUFFER_REG SPI_MEM_MAP_VALUE(0x18) |
||
62 | #define SPI_FIFO_TRANSMIT_CONFIG_REG SPI_MEM_MAP_VALUE(0x1C) |
||
63 | #define SPI_FIFO_TRANSMIT_CONTROL_REG SPI_MEM_MAP_VALUE(0x20) |
||
64 | #define SPI_FIFO_RECEIVE_CONFIG_REG SPI_MEM_MAP_VALUE(0x24) |
||
65 | #define SPI_INTERRUPT_STATUS_REG SPI_MEM_MAP_VALUE(0x28) |
||
66 | #define SPI_INTERRUPT_ENABLE_REG SPI_MEM_MAP_VALUE(0x2C) |
||
67 | |||
68 | #define SPI_TRANSMIT_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x10) |
||
69 | #define SPI_RECEIVE_BUFFER_REG_ADDR (CNS3XXX_SSP_BASE +0x18) |
||
70 | |||
71 | /* Structure for SPI controller of CNS3XXX SOCs */ |
||
72 | struct cns3xxx_spi { |
||
73 | /* bitbang has to be first */ |
||
74 | struct spi_bitbang bitbang; |
||
75 | struct completion done; |
||
76 | wait_queue_head_t wait; |
||
77 | |||
78 | int len; |
||
79 | int count; |
||
80 | int last_in_message_list; |
||
81 | |||
82 | /* data buffers */ |
||
83 | const unsigned char *tx; |
||
84 | unsigned char *rx; |
||
85 | |||
86 | void __iomem *base; |
||
87 | struct spi_master *master; |
||
88 | struct platform_device *pdev; |
||
89 | struct device *dev; |
||
90 | }; |
||
91 | |||
92 | static inline u8 cns3xxx_spi_bus_idle(struct cns3xxx_spi *hw) |
||
93 | { |
||
94 | return ((SPI_SERVICE_STATUS_REG & 0x1) ? 0 : 1); |
||
95 | } |
||
96 | |||
97 | static inline u8 cns3xxx_spi_tx_buffer_empty(struct cns3xxx_spi *hw) |
||
98 | { |
||
99 | return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 3)) ? 1 : 0); |
||
100 | } |
||
101 | |||
102 | static inline u8 cns3xxx_spi_rx_buffer_full(struct cns3xxx_spi *hw) |
||
103 | { |
||
104 | return ((SPI_INTERRUPT_STATUS_REG & (0x1 << 2)) ? 1 : 0); |
||
105 | } |
||
106 | |||
107 | u8 cns3xxx_spi_tx_rx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, |
||
108 | u32 tx_data, u32 * rx_data) |
||
109 | { |
||
110 | u8 rx_channel; |
||
111 | u8 rx_eof; |
||
112 | |||
113 | while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing |
||
114 | |||
115 | while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing |
||
116 | |||
117 | SPI_TRANSMIT_CONTROL_REG &= ~(0x7); |
||
118 | SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2); |
||
119 | |||
120 | SPI_TRANSMIT_BUFFER_REG = tx_data; |
||
121 | |||
122 | while (!cns3xxx_spi_rx_buffer_full(hw)) ; // do nothing |
||
123 | |||
124 | rx_channel = SPI_RECEIVE_CONTROL_REG & 0x3; |
||
125 | rx_eof = (SPI_RECEIVE_CONTROL_REG & (0x1 << 2)) ? 1 : 0; |
||
126 | |||
127 | *rx_data = SPI_RECEIVE_BUFFER_REG; |
||
128 | |||
129 | if ((tx_channel != rx_channel) || (tx_eof != rx_eof)) { |
||
130 | return 0; |
||
131 | } else { |
||
132 | return 1; |
||
133 | } |
||
134 | } |
||
135 | |||
136 | u8 cns3xxx_spi_tx(struct cns3xxx_spi *hw, u8 tx_channel, u8 tx_eof, u32 tx_data) |
||
137 | { |
||
138 | |||
139 | while (!cns3xxx_spi_bus_idle(hw)) ; // do nothing |
||
140 | |||
141 | while (!cns3xxx_spi_tx_buffer_empty(hw)) ; // do nothing |
||
142 | |||
143 | SPI_TRANSMIT_CONTROL_REG &= ~(0x7); |
||
144 | SPI_TRANSMIT_CONTROL_REG |= (tx_channel & 0x3) | ((tx_eof & 0x1) << 2); |
||
145 | |||
146 | SPI_TRANSMIT_BUFFER_REG = tx_data; |
||
147 | |||
148 | return 1; |
||
149 | } |
||
150 | |||
151 | static inline struct cns3xxx_spi *to_hw(struct spi_device *sdev) |
||
152 | { |
||
153 | return spi_master_get_devdata(sdev->master); |
||
154 | } |
||
155 | |||
156 | static int cns3xxx_spi_setup_transfer(struct spi_device *spi, |
||
157 | struct spi_transfer *t) |
||
158 | { |
||
159 | return 0; |
||
160 | } |
||
161 | |||
162 | static void cns3xxx_spi_chipselect(struct spi_device *spi, int value) |
||
163 | { |
||
164 | struct cns3xxx_spi *hw = to_hw(spi); |
||
165 | unsigned int spi_config; |
||
166 | |||
167 | switch (value) { |
||
168 | case BITBANG_CS_INACTIVE: |
||
169 | break; |
||
170 | |||
171 | case BITBANG_CS_ACTIVE: |
||
172 | spi_config = SPI_CONFIGURATION_REG; |
||
173 | |||
174 | if (spi->mode & SPI_CPHA) |
||
175 | spi_config |= (0x1 << 13); |
||
176 | else |
||
177 | spi_config &= ~(0x1 << 13); |
||
178 | |||
179 | if (spi->mode & SPI_CPOL) |
||
180 | spi_config |= (0x1 << 14); |
||
181 | else |
||
182 | spi_config &= ~(0x1 << 14); |
||
183 | |||
184 | /* write new configration */ |
||
185 | SPI_CONFIGURATION_REG = spi_config; |
||
186 | |||
187 | SPI_TRANSMIT_CONTROL_REG &= ~(0x7); |
||
188 | SPI_TRANSMIT_CONTROL_REG |= (spi->chip_select & 0x3); |
||
189 | |||
190 | break; |
||
191 | } |
||
192 | } |
||
193 | |||
194 | static int cns3xxx_spi_setup(struct spi_device *spi) |
||
195 | { |
||
196 | if (!spi->bits_per_word) |
||
197 | spi->bits_per_word = 8; |
||
198 | |||
199 | return 0; |
||
200 | } |
||
201 | |||
202 | static int cns3xxx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) |
||
203 | { |
||
204 | struct cns3xxx_spi *hw = to_hw(spi); |
||
205 | |||
206 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, |
||
207 | t->len); |
||
208 | |||
209 | hw->tx = t->tx_buf; |
||
210 | hw->rx = t->rx_buf; |
||
211 | hw->len = t->len; |
||
212 | hw->count = 0; |
||
213 | hw->last_in_message_list = t->last_in_message_list; |
||
214 | |||
215 | init_completion(&hw->done); |
||
216 | |||
217 | if (hw->tx) { |
||
218 | int i; |
||
219 | u32 rx_data; |
||
220 | for (i = 0; i < (hw->len - 1); i++) { |
||
221 | dev_dbg(&spi->dev, |
||
222 | "[SPI_CNS3XXX_DEBUG] hw->tx[%02d]: 0x%02x\n", i, |
||
223 | hw->tx[i]); |
||
224 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i], |
||
225 | &rx_data); |
||
226 | if (hw->rx) { |
||
227 | hw->rx[i] = rx_data; |
||
228 | dev_dbg(&spi->dev, |
||
229 | "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", |
||
230 | i, hw->rx[i]); |
||
231 | } |
||
232 | } |
||
233 | |||
234 | if (t->last_in_message_list) { |
||
235 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, hw->tx[i], |
||
236 | &rx_data); |
||
237 | if (hw->rx) { |
||
238 | hw->rx[i] = rx_data; |
||
239 | dev_dbg(&spi->dev, |
||
240 | "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", |
||
241 | i, hw->rx[i]); |
||
242 | } |
||
243 | } else { |
||
244 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, hw->tx[i], |
||
245 | &rx_data); |
||
246 | } |
||
247 | goto done; |
||
248 | } |
||
249 | |||
250 | if (hw->rx) { |
||
251 | int i; |
||
252 | u32 rx_data; |
||
253 | for (i = 0; i < (hw->len - 1); i++) { |
||
254 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data); |
||
255 | hw->rx[i] = rx_data; |
||
256 | dev_dbg(&spi->dev, |
||
257 | "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", i, |
||
258 | hw->rx[i]); |
||
259 | } |
||
260 | |||
261 | if (t->last_in_message_list) { |
||
262 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 1, 0xff, &rx_data); |
||
263 | } else { |
||
264 | cns3xxx_spi_tx_rx(hw, spi->chip_select, 0, 0xff, &rx_data); |
||
265 | } |
||
266 | hw->rx[i] = rx_data; |
||
267 | dev_dbg(&spi->dev, "[SPI_CNS3XXX_DEBUG] hw->rx[%02d]: 0x%02x\n", |
||
268 | i, hw->rx[i]); |
||
269 | } |
||
270 | done: |
||
271 | return hw->len; |
||
272 | } |
||
273 | |||
274 | static void __init cns3xxx_spi_initial(struct cns3xxx_spi *hw) |
||
275 | { |
||
276 | SPI_CONFIGURATION_REG = (((0x0 & 0x3) << 0) | /* 8bits shift length */ |
||
277 | (0x0 << 9) | /* SPI mode */ |
||
278 | (0x0 << 10) | /* disable FIFO */ |
||
279 | (0x1 << 11) | /* SPI master mode */ |
||
280 | (0x0 << 12) | /* disable SPI loopback mode */ |
||
281 | (0x1 << 13) | /* clock phase */ |
||
282 | (0x1 << 14) | /* clock polarity */ |
||
283 | (0x0 << 24) | /* disable - SPI data swap */ |
||
284 | (0x1 << 29) | /* enable - 2IO Read mode */ |
||
285 | (0x0 << 30) | /* disable - SPI high speed read for system boot up */ |
||
286 | (0x0 << 31)); /* disable - SPI */ |
||
287 | |||
288 | /* Set SPI bit rate PCLK/2 */ |
||
289 | SPI_BIT_RATE_CONTROL_REG = 0x1; |
||
290 | |||
291 | /* Set SPI Tx channel 0 */ |
||
292 | SPI_TRANSMIT_CONTROL_REG = 0x0; |
||
293 | |||
294 | /* Set Tx FIFO Threshold, Tx FIFO has 2 words */ |
||
295 | SPI_FIFO_TRANSMIT_CONFIG_REG &= ~(0x03 << 4); |
||
296 | SPI_FIFO_TRANSMIT_CONFIG_REG |= ((0x0 & 0x03) << 4); |
||
297 | |||
298 | /* Set Rx FIFO Threshold, Rx FIFO has 2 words */ |
||
299 | SPI_FIFO_RECEIVE_CONFIG_REG &= ~(0x03 << 4); |
||
300 | SPI_FIFO_RECEIVE_CONFIG_REG |= ((0x0 & 0x03) << 4); |
||
301 | |||
302 | /* Disable all interrupt */ |
||
303 | SPI_INTERRUPT_ENABLE_REG = 0x0; |
||
304 | |||
305 | /* Clear spurious interrupt sources */ |
||
306 | SPI_INTERRUPT_STATUS_REG = (0x0F << 4); |
||
307 | |||
308 | /* Enable SPI */ |
||
309 | SPI_CONFIGURATION_REG |= (0x1 << 31); |
||
310 | |||
311 | return; |
||
312 | } |
||
313 | |||
314 | static int cns3xxx_spi_probe(struct platform_device *pdev) |
||
315 | { |
||
316 | struct spi_master *master; |
||
317 | struct cns3xxx_spi *hw; |
||
318 | struct resource *res; |
||
319 | int err = 0; |
||
320 | |||
321 | printk("%s: setup CNS3XXX SPI Controller\n", __FUNCTION__); |
||
322 | |||
323 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||
324 | if (!res) |
||
325 | return -ENODEV; |
||
326 | |||
327 | /* Allocate master with space for cns3xxx_spi */ |
||
328 | master = spi_alloc_master(&pdev->dev, sizeof(struct cns3xxx_spi)); |
||
329 | if (master == NULL) { |
||
330 | dev_err(&pdev->dev, "No memory for spi_master\n"); |
||
331 | err = -ENOMEM; |
||
332 | goto err_nomem; |
||
333 | } |
||
334 | |||
335 | hw = spi_master_get_devdata(master); |
||
336 | memset(hw, 0, sizeof(struct cns3xxx_spi)); |
||
337 | |||
338 | hw->master = spi_master_get(master); |
||
339 | hw->dev = &pdev->dev; |
||
340 | |||
341 | hw->base = devm_ioremap_resource(hw->dev, res); |
||
342 | if (IS_ERR(hw->base)) { |
||
343 | dev_err(hw->dev, "Unable to map registers\n"); |
||
344 | err = PTR_ERR(hw->base); |
||
345 | goto err_register; |
||
346 | } |
||
347 | |||
348 | platform_set_drvdata(pdev, hw); |
||
349 | init_completion(&hw->done); |
||
350 | |||
351 | /* setup the master state. */ |
||
352 | |||
353 | master->num_chipselect = 4; |
||
354 | master->bus_num = 1; |
||
355 | |||
356 | /* setup the state for the bitbang driver */ |
||
357 | |||
358 | hw->bitbang.master = hw->master; |
||
359 | hw->bitbang.setup_transfer = cns3xxx_spi_setup_transfer; |
||
360 | hw->bitbang.chipselect = cns3xxx_spi_chipselect; |
||
361 | hw->bitbang.txrx_bufs = cns3xxx_spi_txrx; |
||
362 | hw->bitbang.master->setup = cns3xxx_spi_setup; |
||
363 | |||
364 | dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); |
||
365 | |||
366 | /* SPI controller initializations */ |
||
367 | cns3xxx_spi_initial(hw); |
||
368 | |||
369 | /* register SPI controller */ |
||
370 | |||
371 | err = spi_bitbang_start(&hw->bitbang); |
||
372 | if (err) { |
||
373 | dev_err(&pdev->dev, "Failed to register SPI master\n"); |
||
374 | goto err_register; |
||
375 | } |
||
376 | |||
377 | return 0; |
||
378 | |||
379 | err_register: |
||
380 | spi_master_put(hw->master);; |
||
381 | |||
382 | err_nomem: |
||
383 | return err; |
||
384 | } |
||
385 | |||
386 | static int cns3xxx_spi_remove(struct platform_device *dev) |
||
387 | { |
||
388 | struct cns3xxx_spi *hw = platform_get_drvdata(dev); |
||
389 | |||
390 | platform_set_drvdata(dev, NULL); |
||
391 | |||
392 | spi_unregister_master(hw->master); |
||
393 | |||
394 | spi_master_put(hw->master); |
||
395 | return 0; |
||
396 | } |
||
397 | |||
398 | #ifdef CONFIG_PM |
||
399 | |||
400 | static int cns3xxx_spi_suspend(struct platform_device *pdev, pm_message_t msg) |
||
401 | { |
||
402 | struct cns3xxx_spi *hw = platform_get_drvdata(pdev); |
||
403 | |||
404 | return 0; |
||
405 | } |
||
406 | |||
407 | static int cns3xxx_spi_resume(struct platform_device *pdev) |
||
408 | { |
||
409 | struct cns3xxx_spi *hw = platform_get_drvdata(pdev); |
||
410 | |||
411 | return 0; |
||
412 | } |
||
413 | |||
414 | #else |
||
415 | #define cns3xxx_spi_suspend NULL |
||
416 | #define cns3xxx_spi_resume NULL |
||
417 | #endif |
||
418 | |||
419 | static struct platform_driver cns3xxx_spi_driver = { |
||
420 | .probe = cns3xxx_spi_probe, |
||
421 | .remove = cns3xxx_spi_remove, |
||
422 | .suspend = cns3xxx_spi_suspend, |
||
423 | .resume = cns3xxx_spi_resume, |
||
424 | .driver = { |
||
425 | .name = "cns3xxx_spi", |
||
426 | .owner = THIS_MODULE, |
||
427 | }, |
||
428 | }; |
||
429 | |||
430 | static int __init cns3xxx_spi_init(void) |
||
431 | { |
||
432 | return platform_driver_register(&cns3xxx_spi_driver); |
||
433 | } |
||
434 | |||
435 | static void __exit cns3xxx_spi_exit(void) |
||
436 | { |
||
437 | platform_driver_unregister(&cns3xxx_spi_driver); |
||
438 | } |
||
439 | |||
440 | module_init(cns3xxx_spi_init); |
||
441 | module_exit(cns3xxx_spi_exit); |
||
442 | |||
443 | MODULE_AUTHOR("Cavium Networks"); |
||
444 | MODULE_DESCRIPTION("CNS3XXX SPI Controller Driver"); |
||
445 | MODULE_LICENSE("GPL"); |
||
446 | MODULE_ALIAS("platform:cns3xxx_spi"); |
||
447 | |||
448 | EXPORT_SYMBOL_GPL(cns3xxx_spi_tx_rx); |