aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-bcm63xx.c
diff options
context:
space:
mode:
authorJonas Gorski <jogo@openwrt.org>2013-02-03 09:15:13 -0500
committerGrant Likely <grant.likely@secretlab.ca>2013-02-05 12:14:33 -0500
commitb17de076062a677b11ec5054d61184d0134e851f (patch)
tree67f8ff78a15b99fe2ec170ce400607af63d9c6e6 /drivers/spi/spi-bcm63xx.c
parent32310aaf5c410dd3dc701556cc9bbeff0847c7c9 (diff)
spi/bcm63xx: work around inability to keep CS up
This SPI controller does not support keeping CS asserted after sending a transfer. Since messages expected on this SPI controller are rather short, we can work around it for normal use cases by sending all transfers at once in a big full duplex stream. This means that we cannot change the speed between transfers if they require CS to be kept asserted, but these would have been rejected before anyway because of the inability of keeping CS asserted. Signed-off-by: Jonas Gorski <jogo@openwrt.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/spi/spi-bcm63xx.c')
-rw-r--r--drivers/spi/spi-bcm63xx.c134
1 files changed, 106 insertions, 28 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 27667c17ce90..9578af782a77 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -37,6 +37,8 @@
37 37
38#define PFX KBUILD_MODNAME 38#define PFX KBUILD_MODNAME
39 39
40#define BCM63XX_SPI_MAX_PREPEND 15
41
40struct bcm63xx_spi { 42struct bcm63xx_spi {
41 struct completion done; 43 struct completion done;
42 44
@@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
169 return 0; 171 return 0;
170} 172}
171 173
172static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) 174static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
175 unsigned int num_transfers)
173{ 176{
174 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 177 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
175 u16 msg_ctl; 178 u16 msg_ctl;
176 u16 cmd; 179 u16 cmd;
177 u8 rx_tail; 180 u8 rx_tail;
178 unsigned int timeout = 0; 181 unsigned int i, timeout = 0, prepend_len = 0, len = 0;
182 struct spi_transfer *t = first;
183 bool do_rx = false;
184 bool do_tx = false;
179 185
180 /* Disable the CMD_DONE interrupt */ 186 /* Disable the CMD_DONE interrupt */
181 bcm_spi_writeb(bs, 0, SPI_INT_MASK); 187 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
@@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
183 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 189 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
184 t->tx_buf, t->rx_buf, t->len); 190 t->tx_buf, t->rx_buf, t->len);
185 191
186 if (t->tx_buf) 192 if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
187 memcpy_toio(bs->tx_io, t->tx_buf, t->len); 193 prepend_len = t->len;
194
195 /* prepare the buffer */
196 for (i = 0; i < num_transfers; i++) {
197 if (t->tx_buf) {
198 do_tx = true;
199 memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
200
201 /* don't prepend more than one tx */
202 if (t != first)
203 prepend_len = 0;
204 }
205
206 if (t->rx_buf) {
207 do_rx = true;
208 /* prepend is half-duplex write only */
209 if (t == first)
210 prepend_len = 0;
211 }
212
213 len += t->len;
214
215 t = list_entry(t->transfer_list.next, struct spi_transfer,
216 transfer_list);
217 }
218
219 len -= prepend_len;
188 220
189 init_completion(&bs->done); 221 init_completion(&bs->done);
190 222
191 /* Fill in the Message control register */ 223 /* Fill in the Message control register */
192 msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); 224 msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
193 225
194 if (t->rx_buf && t->tx_buf) 226 if (do_rx && do_tx && prepend_len == 0)
195 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 227 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
196 else if (t->rx_buf) 228 else if (do_rx)
197 msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 229 msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
198 else if (t->tx_buf) 230 else if (do_tx)
199 msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 231 msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
200 232
201 switch (bs->msg_ctl_width) { 233 switch (bs->msg_ctl_width) {
@@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
209 241
210 /* Issue the transfer */ 242 /* Issue the transfer */
211 cmd = SPI_CMD_START_IMMEDIATE; 243 cmd = SPI_CMD_START_IMMEDIATE;
212 cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 244 cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
213 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); 245 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
214 bcm_spi_writew(bs, cmd, SPI_CMD); 246 bcm_spi_writew(bs, cmd, SPI_CMD);
215 247
@@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
223 /* read out all data */ 255 /* read out all data */
224 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); 256 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
225 257
258 if (do_rx && rx_tail != len)
259 return -EIO;
260
261 if (!rx_tail)
262 return 0;
263
264 len = 0;
265 t = first;
226 /* Read out all the data */ 266 /* Read out all the data */
227 if (rx_tail) 267 for (i = 0; i < num_transfers; i++) {
228 memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail); 268 if (t->rx_buf)
269 memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
270
271 if (t != first || prepend_len == 0)
272 len += t->len;
273
274 t = list_entry(t->transfer_list.next, struct spi_transfer,
275 transfer_list);
276 }
229 277
230 return 0; 278 return 0;
231} 279}
@@ -252,46 +300,76 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
252 struct spi_message *m) 300 struct spi_message *m)
253{ 301{
254 struct bcm63xx_spi *bs = spi_master_get_devdata(master); 302 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
255 struct spi_transfer *t; 303 struct spi_transfer *t, *first = NULL;
256 struct spi_device *spi = m->spi; 304 struct spi_device *spi = m->spi;
257 int status = 0; 305 int status = 0;
258 306 unsigned int n_transfers = 0, total_len = 0;
307 bool can_use_prepend = false;
308
309 /*
310 * This SPI controller does not support keeping CS active after a
311 * transfer.
312 * Work around this by merging as many transfers we can into one big
313 * full-duplex transfers.
314 */
259 list_for_each_entry(t, &m->transfers, transfer_list) { 315 list_for_each_entry(t, &m->transfers, transfer_list) {
260 status = bcm63xx_spi_check_transfer(spi, t); 316 status = bcm63xx_spi_check_transfer(spi, t);
261 if (status < 0) 317 if (status < 0)
262 goto exit; 318 goto exit;
263 319
320 if (!first)
321 first = t;
322
323 n_transfers++;
324 total_len += t->len;
325
326 if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
327 first->len <= BCM63XX_SPI_MAX_PREPEND)
328 can_use_prepend = true;
329 else if (can_use_prepend && t->tx_buf)
330 can_use_prepend = false;
331
264 /* we can only transfer one fifo worth of data */ 332 /* we can only transfer one fifo worth of data */
265 if (t->len > bs->fifo_size) { 333 if ((can_use_prepend &&
334 total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
335 (!can_use_prepend && total_len > bs->fifo_size)) {
266 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 336 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
267 t->len, bs->fifo_size); 337 total_len, bs->fifo_size);
268 status = -EINVAL; 338 status = -EINVAL;
269 goto exit; 339 goto exit;
270 } 340 }
271 341
272 /* CS will be deasserted directly after transfer */ 342 /* all combined transfers have to have the same speed */
273 if (t->delay_usecs) { 343 if (t->speed_hz != first->speed_hz) {
274 dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 344 dev_err(&spi->dev, "unable to change speed between transfers\n");
275 status = -EINVAL; 345 status = -EINVAL;
276 goto exit; 346 goto exit;
277 } 347 }
278 348
279 if (!t->cs_change && 349 /* CS will be deasserted directly after transfer */
280 !list_is_last(&t->transfer_list, &m->transfers)) { 350 if (t->delay_usecs) {
281 dev_err(&spi->dev, "unable to keep CS asserted between transfers\n"); 351 dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
282 status = -EINVAL; 352 status = -EINVAL;
283 goto exit; 353 goto exit;
284 } 354 }
285 355
286 /* configure adapter for a new transfer */ 356 if (t->cs_change ||
287 bcm63xx_spi_setup_transfer(spi, t); 357 list_is_last(&t->transfer_list, &m->transfers)) {
358 /* configure adapter for a new transfer */
359 bcm63xx_spi_setup_transfer(spi, first);
288 360
289 /* send the data */ 361 /* send the data */
290 status = bcm63xx_txrx_bufs(spi, t); 362 status = bcm63xx_txrx_bufs(spi, first, n_transfers);
291 if (status) 363 if (status)
292 goto exit; 364 goto exit;
365
366 m->actual_length += total_len;
293 367
294 m->actual_length += t->len; 368 first = NULL;
369 n_transfers = 0;
370 total_len = 0;
371 can_use_prepend = false;
372 }
295 } 373 }
296exit: 374exit:
297 m->status = status; 375 m->status = status;