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