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