aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-mpc512x-psc.c
diff options
context:
space:
mode:
authorGerhard Sittig <gsi@denx.de>2013-06-03 08:03:50 -0400
committerMark Brown <broonie@linaro.org>2013-06-04 13:22:47 -0400
commit5df24ea63d1eb1b3b0556817bdaf378c19f196ea (patch)
treec747df0a3d7ee7cd8f78ddecb01776a946fda64e /drivers/spi/spi-mpc512x-psc.c
parentc36e93a0bdc7df31543a6950454dbd26b56c9015 (diff)
spi: mpc512x: improve throughput in the RX/TX func
change the MPC512x SPI controller's transmission routine to increase throughput: allow the RX byte counter to "lag behind" the TX byte counter while iterating over the transfer's data, only wait for the remaining RX bytes at the very end of the transfer this approach eliminates delays in the milliseconds range, transfer times for e.g. 16MB of SPI flash data dropped from 31s to 9s, correct operation was tested by continuously transferring and comparing data from an SPI flash (more than 200GB in some 45 hours) background information on the motivation: one might assume that all the RX data should have been received when the TX data was sent, given the fact that we are the SPI master and provide all of the clock, but in practise there's a difference the ISR is triggered when the TX FIFO became empty, while transmission of the last item still occurs (from the TX hold and shift registers), sampling RX data on the opposite clock edge compared to the TX data adds another delay (half a bit period), and RX data needs to propagate from the reception buffer to the RX FIFO depending on the specific SoC implementation to cut it short: a difference between TX and RX byte counters during transmission is not just acceptable but should be considered the regular case, only the very end of the transfer needs to make sure that all of the RX data was received before deasserting the chip select and telling the caller that transmission has completed Signed-off-by: Gerhard Sittig <gsi@denx.de> Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'drivers/spi/spi-mpc512x-psc.c')
-rw-r--r--drivers/spi/spi-mpc512x-psc.c141
1 files changed, 107 insertions, 34 deletions
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 759a937d5fa7..53c7899a8aba 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -137,6 +137,7 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
137 struct mpc52xx_psc __iomem *psc = mps->psc; 137 struct mpc52xx_psc __iomem *psc = mps->psc;
138 struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; 138 struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
139 size_t tx_len = t->len; 139 size_t tx_len = t->len;
140 size_t rx_len = t->len;
140 u8 *tx_buf = (u8 *)t->tx_buf; 141 u8 *tx_buf = (u8 *)t->tx_buf;
141 u8 *rx_buf = (u8 *)t->rx_buf; 142 u8 *rx_buf = (u8 *)t->rx_buf;
142 143
@@ -150,57 +151,129 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
150 /* enable transmiter/receiver */ 151 /* enable transmiter/receiver */
151 out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); 152 out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
152 153
153 while (tx_len) { 154 while (rx_len || tx_len) {
154 size_t txcount; 155 size_t txcount;
155 int i;
156 u8 data; 156 u8 data;
157 size_t fifosz; 157 size_t fifosz;
158 size_t rxcount; 158 size_t rxcount;
159 int rxtries;
159 160
160 /* 161 /*
161 * The number of bytes that can be sent at a time 162 * send the TX bytes in as large a chunk as possible
162 * depends on the fifo size. 163 * but neither exceed the TX nor the RX FIFOs
163 */ 164 */
164 fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->txsz)); 165 fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->txsz));
165 txcount = min(fifosz, tx_len); 166 txcount = min(fifosz, tx_len);
167 fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->rxsz));
168 fifosz -= in_be32(&fifo->rxcnt) + 1;
169 txcount = min(fifosz, txcount);
170 if (txcount) {
171
172 /* fill the TX FIFO */
173 while (txcount-- > 0) {
174 data = tx_buf ? *tx_buf++ : 0;
175 if (tx_len == EOFBYTE && t->cs_change)
176 setbits32(&fifo->txcmd,
177 MPC512x_PSC_FIFO_EOF);
178 out_8(&fifo->txdata_8, data);
179 tx_len--;
180 }
166 181
167 for (i = txcount; i > 0; i--) { 182 /* have the ISR trigger when the TX FIFO is empty */
168 data = tx_buf ? *tx_buf++ : 0; 183 INIT_COMPLETION(mps->txisrdone);
169 if (tx_len == EOFBYTE && t->cs_change) 184 out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
170 setbits32(&fifo->txcmd, MPC512x_PSC_FIFO_EOF); 185 out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY);
171 out_8(&fifo->txdata_8, data); 186 wait_for_completion(&mps->txisrdone);
172 tx_len--;
173 } 187 }
174 188
175 INIT_COMPLETION(mps->txisrdone); 189 /*
176 190 * consume as much RX data as the FIFO holds, while we
177 /* interrupt on tx fifo empty */ 191 * iterate over the transfer's TX data length
178 out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY); 192 *
179 out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY); 193 * only insist in draining all the remaining RX bytes
180 194 * when the TX bytes were exhausted (that's at the very
181 wait_for_completion(&mps->txisrdone); 195 * end of this transfer, not when still iterating over
182 196 * the transfer's chunks)
183 mdelay(1); 197 */
198 rxtries = 50;
199 do {
200
201 /*
202 * grab whatever was in the FIFO when we started
203 * looking, don't bother fetching what was added to
204 * the FIFO while we read from it -- we'll return
205 * here eventually and prefer sending out remaining
206 * TX data
207 */
208 fifosz = in_be32(&fifo->rxcnt);
209 rxcount = min(fifosz, rx_len);
210 while (rxcount-- > 0) {
211 data = in_8(&fifo->rxdata_8);
212 if (rx_buf)
213 *rx_buf++ = data;
214 rx_len--;
215 }
184 216
185 /* rx fifo should have txcount bytes in it */ 217 /*
186 rxcount = in_be32(&fifo->rxcnt); 218 * come back later if there still is TX data to send,
187 if (rxcount != txcount) 219 * bail out of the RX drain loop if all of the TX data
188 mdelay(1); 220 * was sent and all of the RX data was received (i.e.
221 * when the transmission has completed)
222 */
223 if (tx_len)
224 break;
225 if (!rx_len)
226 break;
189 227
190 rxcount = in_be32(&fifo->rxcnt); 228 /*
191 if (rxcount != txcount) { 229 * TX data transmission has completed while RX data
192 dev_warn(&spi->dev, "expected %d bytes in rx fifo " 230 * is still pending -- that's a transient situation
193 "but got %d\n", txcount, rxcount); 231 * which depends on wire speed and specific
232 * hardware implementation details (buffering) yet
233 * should resolve very quickly
234 *
235 * just yield for a moment to not hog the CPU for
236 * too long when running SPI at low speed
237 *
238 * the timeout range is rather arbitrary and tries
239 * to balance throughput against system load; the
240 * chosen values result in a minimal timeout of 50
241 * times 10us and thus work at speeds as low as
242 * some 20kbps, while the maximum timeout at the
243 * transfer's end could be 5ms _if_ nothing else
244 * ticks in the system _and_ RX data still wasn't
245 * received, which only occurs in situations that
246 * are exceptional; removing the unpredictability
247 * of the timeout either decreases throughput
248 * (longer timeouts), or puts more load on the
249 * system (fixed short timeouts) or requires the
250 * use of a timeout API instead of a counter and an
251 * unknown inner delay
252 */
253 usleep_range(10, 100);
254
255 } while (--rxtries > 0);
256 if (!tx_len && rx_len && !rxtries) {
257 /*
258 * not enough RX bytes even after several retries
259 * and the resulting rather long timeout?
260 */
261 rxcount = in_be32(&fifo->rxcnt);
262 dev_warn(&spi->dev,
263 "short xfer, missing %zd RX bytes, FIFO level %zd\n",
264 rx_len, rxcount);
194 } 265 }
195 266
196 rxcount = min(rxcount, txcount); 267 /*
197 for (i = rxcount; i > 0; i--) { 268 * drain and drop RX data which "should not be there" in
198 data = in_8(&fifo->rxdata_8); 269 * the first place, for undisturbed transmission this turns
199 if (rx_buf) 270 * into a NOP (except for the FIFO level fetch)
200 *rx_buf++ = data; 271 */
272 if (!tx_len && !rx_len) {
273 while (in_be32(&fifo->rxcnt))
274 in_8(&fifo->rxdata_8);
201 } 275 }
202 while (in_be32(&fifo->rxcnt)) 276
203 in_8(&fifo->rxdata_8);
204 } 277 }
205 /* disable transmiter/receiver and fifo interrupt */ 278 /* disable transmiter/receiver and fifo interrupt */
206 out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); 279 out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);