Commit | Line | Data |
---|---|---|
529d6dad SB |
1 | /* |
2 | * Copyright (C) ST-Ericsson AB 2010 | |
3 | * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com | |
4 | * Author: Daniel Martensson / Daniel.Martensson@stericsson.com | |
5 | * License terms: GNU General Public License (GPL) version 2. | |
6 | */ | |
7 | #include <linux/version.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/device.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/string.h> | |
13 | #include <linux/semaphore.h> | |
14 | #include <linux/workqueue.h> | |
15 | #include <linux/completion.h> | |
16 | #include <linux/list.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/dma-mapping.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/debugfs.h> | |
22 | #include <net/caif/caif_spi.h> | |
23 | ||
24 | #ifndef CONFIG_CAIF_SPI_SYNC | |
25 | #define SPI_DATA_POS SPI_CMD_SZ | |
26 | static inline int forward_to_spi_cmd(struct cfspi *cfspi) | |
27 | { | |
28 | return cfspi->rx_cpck_len; | |
29 | } | |
30 | #else | |
31 | #define SPI_DATA_POS 0 | |
32 | static inline int forward_to_spi_cmd(struct cfspi *cfspi) | |
33 | { | |
34 | return 0; | |
35 | } | |
36 | #endif | |
37 | ||
38 | int spi_frm_align = 2; | |
39 | int spi_up_head_align = 1; | |
40 | int spi_up_tail_align; | |
41 | int spi_down_head_align = 3; | |
42 | int spi_down_tail_align = 1; | |
43 | ||
44 | #ifdef CONFIG_DEBUG_FS | |
45 | static inline void debugfs_store_prev(struct cfspi *cfspi) | |
46 | { | |
47 | /* Store previous command for debugging reasons.*/ | |
48 | cfspi->pcmd = cfspi->cmd; | |
49 | /* Store previous transfer. */ | |
50 | cfspi->tx_ppck_len = cfspi->tx_cpck_len; | |
51 | cfspi->rx_ppck_len = cfspi->rx_cpck_len; | |
52 | } | |
53 | #else | |
54 | static inline void debugfs_store_prev(struct cfspi *cfspi) | |
55 | { | |
56 | } | |
57 | #endif | |
58 | ||
59 | void cfspi_xfer(struct work_struct *work) | |
60 | { | |
61 | struct cfspi *cfspi; | |
62 | u8 *ptr = NULL; | |
63 | unsigned long flags; | |
64 | int ret; | |
65 | cfspi = container_of(work, struct cfspi, work); | |
66 | ||
67 | /* Initialize state. */ | |
68 | cfspi->cmd = SPI_CMD_EOT; | |
69 | ||
70 | for (;;) { | |
71 | ||
72 | cfspi_dbg_state(cfspi, CFSPI_STATE_WAITING); | |
73 | ||
74 | /* Wait for master talk or transmit event. */ | |
75 | wait_event_interruptible(cfspi->wait, | |
76 | test_bit(SPI_XFER, &cfspi->state) || | |
77 | test_bit(SPI_TERMINATE, &cfspi->state)); | |
78 | ||
79 | if (test_bit(SPI_TERMINATE, &cfspi->state)) | |
80 | return; | |
81 | ||
82 | #if CFSPI_DBG_PREFILL | |
83 | /* Prefill buffers for easier debugging. */ | |
84 | memset(cfspi->xfer.va_tx, 0xFF, SPI_DMA_BUF_LEN); | |
85 | memset(cfspi->xfer.va_rx, 0xFF, SPI_DMA_BUF_LEN); | |
86 | #endif /* CFSPI_DBG_PREFILL */ | |
87 | ||
88 | cfspi_dbg_state(cfspi, CFSPI_STATE_AWAKE); | |
89 | ||
90 | /* Check whether we have a committed frame. */ | |
91 | if (cfspi->tx_cpck_len) { | |
92 | int len; | |
93 | ||
94 | cfspi_dbg_state(cfspi, CFSPI_STATE_FETCH_PKT); | |
95 | ||
96 | /* Copy commited SPI frames after the SPI indication. */ | |
97 | ptr = (u8 *) cfspi->xfer.va_tx; | |
98 | ptr += SPI_IND_SZ; | |
99 | len = cfspi_xmitfrm(cfspi, ptr, cfspi->tx_cpck_len); | |
100 | WARN_ON(len != cfspi->tx_cpck_len); | |
101 | } | |
102 | ||
103 | cfspi_dbg_state(cfspi, CFSPI_STATE_GET_NEXT); | |
104 | ||
105 | /* Get length of next frame to commit. */ | |
106 | cfspi->tx_npck_len = cfspi_xmitlen(cfspi); | |
107 | ||
108 | WARN_ON(cfspi->tx_npck_len > SPI_DMA_BUF_LEN); | |
109 | ||
110 | /* | |
111 | * Add indication and length at the beginning of the frame, | |
112 | * using little endian. | |
113 | */ | |
114 | ptr = (u8 *) cfspi->xfer.va_tx; | |
115 | *ptr++ = SPI_CMD_IND; | |
116 | *ptr++ = (SPI_CMD_IND & 0xFF00) >> 8; | |
117 | *ptr++ = cfspi->tx_npck_len & 0x00FF; | |
118 | *ptr++ = (cfspi->tx_npck_len & 0xFF00) >> 8; | |
119 | ||
120 | /* Calculate length of DMAs. */ | |
121 | cfspi->xfer.tx_dma_len = cfspi->tx_cpck_len + SPI_IND_SZ; | |
122 | cfspi->xfer.rx_dma_len = cfspi->rx_cpck_len + SPI_CMD_SZ; | |
123 | ||
124 | /* Add SPI TX frame alignment padding, if necessary. */ | |
125 | if (cfspi->tx_cpck_len && | |
126 | (cfspi->xfer.tx_dma_len % spi_frm_align)) { | |
127 | ||
128 | cfspi->xfer.tx_dma_len += spi_frm_align - | |
129 | (cfspi->xfer.tx_dma_len % spi_frm_align); | |
130 | } | |
131 | ||
132 | /* Add SPI RX frame alignment padding, if necessary. */ | |
133 | if (cfspi->rx_cpck_len && | |
134 | (cfspi->xfer.rx_dma_len % spi_frm_align)) { | |
135 | ||
136 | cfspi->xfer.rx_dma_len += spi_frm_align - | |
137 | (cfspi->xfer.rx_dma_len % spi_frm_align); | |
138 | } | |
139 | ||
140 | cfspi_dbg_state(cfspi, CFSPI_STATE_INIT_XFER); | |
141 | ||
142 | /* Start transfer. */ | |
143 | ret = cfspi->dev->init_xfer(&cfspi->xfer, cfspi->dev); | |
144 | WARN_ON(ret); | |
145 | ||
146 | cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_ACTIVE); | |
147 | ||
148 | /* | |
149 | * TODO: We might be able to make an assumption if this is the | |
150 | * first loop. Make sure that minimum toggle time is respected. | |
151 | */ | |
152 | udelay(MIN_TRANSITION_TIME_USEC); | |
153 | ||
154 | cfspi_dbg_state(cfspi, CFSPI_STATE_SIG_ACTIVE); | |
155 | ||
156 | /* Signal that we are ready to recieve data. */ | |
157 | cfspi->dev->sig_xfer(true, cfspi->dev); | |
158 | ||
159 | cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_XFER_DONE); | |
160 | ||
161 | /* Wait for transfer completion. */ | |
162 | wait_for_completion(&cfspi->comp); | |
163 | ||
164 | cfspi_dbg_state(cfspi, CFSPI_STATE_XFER_DONE); | |
165 | ||
166 | if (cfspi->cmd == SPI_CMD_EOT) { | |
167 | /* | |
168 | * Clear the master talk bit. A xfer is always at | |
169 | * least two bursts. | |
170 | */ | |
171 | clear_bit(SPI_SS_ON, &cfspi->state); | |
172 | } | |
173 | ||
174 | cfspi_dbg_state(cfspi, CFSPI_STATE_WAIT_INACTIVE); | |
175 | ||
176 | /* Make sure that the minimum toggle time is respected. */ | |
177 | if (SPI_XFER_TIME_USEC(cfspi->xfer.tx_dma_len, | |
178 | cfspi->dev->clk_mhz) < | |
179 | MIN_TRANSITION_TIME_USEC) { | |
180 | ||
181 | udelay(MIN_TRANSITION_TIME_USEC - | |
182 | SPI_XFER_TIME_USEC | |
183 | (cfspi->xfer.tx_dma_len, cfspi->dev->clk_mhz)); | |
184 | } | |
185 | ||
186 | cfspi_dbg_state(cfspi, CFSPI_STATE_SIG_INACTIVE); | |
187 | ||
188 | /* De-assert transfer signal. */ | |
189 | cfspi->dev->sig_xfer(false, cfspi->dev); | |
190 | ||
191 | /* Check whether we received a CAIF packet. */ | |
192 | if (cfspi->rx_cpck_len) { | |
193 | int len; | |
194 | ||
195 | cfspi_dbg_state(cfspi, CFSPI_STATE_DELIVER_PKT); | |
196 | ||
197 | /* Parse SPI frame. */ | |
198 | ptr = ((u8 *)(cfspi->xfer.va_rx + SPI_DATA_POS)); | |
199 | ||
200 | len = cfspi_rxfrm(cfspi, ptr, cfspi->rx_cpck_len); | |
201 | WARN_ON(len != cfspi->rx_cpck_len); | |
202 | } | |
203 | ||
204 | /* Check the next SPI command and length. */ | |
205 | ptr = (u8 *) cfspi->xfer.va_rx; | |
206 | ||
207 | ptr += forward_to_spi_cmd(cfspi); | |
208 | ||
209 | cfspi->cmd = *ptr++; | |
210 | cfspi->cmd |= ((*ptr++) << 8) & 0xFF00; | |
211 | cfspi->rx_npck_len = *ptr++; | |
212 | cfspi->rx_npck_len |= ((*ptr++) << 8) & 0xFF00; | |
213 | ||
214 | WARN_ON(cfspi->rx_npck_len > SPI_DMA_BUF_LEN); | |
215 | WARN_ON(cfspi->cmd > SPI_CMD_EOT); | |
216 | ||
217 | debugfs_store_prev(cfspi); | |
218 | ||
219 | /* Check whether the master issued an EOT command. */ | |
220 | if (cfspi->cmd == SPI_CMD_EOT) { | |
221 | /* Reset state. */ | |
222 | cfspi->tx_cpck_len = 0; | |
223 | cfspi->rx_cpck_len = 0; | |
224 | } else { | |
225 | /* Update state. */ | |
226 | cfspi->tx_cpck_len = cfspi->tx_npck_len; | |
227 | cfspi->rx_cpck_len = cfspi->rx_npck_len; | |
228 | } | |
229 | ||
230 | /* | |
231 | * Check whether we need to clear the xfer bit. | |
232 | * Spin lock needed for packet insertion. | |
233 | * Test and clear of different bits | |
234 | * are not supported. | |
235 | */ | |
236 | spin_lock_irqsave(&cfspi->lock, flags); | |
237 | if (cfspi->cmd == SPI_CMD_EOT && !cfspi_xmitlen(cfspi) | |
238 | && !test_bit(SPI_SS_ON, &cfspi->state)) | |
239 | clear_bit(SPI_XFER, &cfspi->state); | |
240 | ||
241 | spin_unlock_irqrestore(&cfspi->lock, flags); | |
242 | } | |
243 | } | |
244 | ||
245 | struct platform_driver cfspi_spi_driver = { | |
246 | .probe = cfspi_spi_probe, | |
247 | .remove = cfspi_spi_remove, | |
248 | .driver = { | |
249 | .name = "cfspi_sspi", | |
250 | .owner = THIS_MODULE, | |
251 | }, | |
252 | }; |