diff options
author | David Brownell <david-b@pacbell.net> | 2007-07-17 07:04:08 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:05 -0400 |
commit | defbd3b4bbd9d85a68529f829f4ee39899c318dc (patch) | |
tree | 51609b853c11c8100b054b167e44dbac1f9c9095 /drivers | |
parent | 8da0859a246838c81fe57d952b91d419e9c44179 (diff) |
atmel_spi: don't always deselect chip between messages
Update chipselect handling for atmel_spi:
* Teach it how to leave chipselect active between messages; this
helps various drivers work better.
* Cope with at91rm0200 errata: nCS0 can't be managed with GPIOs.
The MR.PCS value is now updated whenever a chipselect changes.
(This requires SPI pinmux init for that controller to change,
and also testing on rm9200; doesn't break at91sam9 or avr32.)
* Fix minor glitches: spi_setup() must leave chipselects inactive,
as must removal of the spi_device.
Also tweak diagnostic messaging to be a bit more useful.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/atmel_spi.c | 127 |
1 files changed, 103 insertions, 24 deletions
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 01ad6320d96c..ad144054da30 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c | |||
@@ -46,6 +46,7 @@ struct atmel_spi { | |||
46 | struct clk *clk; | 46 | struct clk *clk; |
47 | struct platform_device *pdev; | 47 | struct platform_device *pdev; |
48 | unsigned new_1:1; | 48 | unsigned new_1:1; |
49 | struct spi_device *stay; | ||
49 | 50 | ||
50 | u8 stopping; | 51 | u8 stopping; |
51 | struct list_head queue; | 52 | struct list_head queue; |
@@ -62,29 +63,62 @@ struct atmel_spi { | |||
62 | /* | 63 | /* |
63 | * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby | 64 | * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby |
64 | * they assume that spi slave device state will not change on deselect, so | 65 | * they assume that spi slave device state will not change on deselect, so |
65 | * that automagic deselection is OK. Not so! Workaround uses nCSx pins | 66 | * that automagic deselection is OK. ("NPCSx rises if no data is to be |
66 | * as GPIOs; or newer controllers have CSAAT and friends. | 67 | * transmitted") Not so! Workaround uses nCSx pins as GPIOs; or newer |
68 | * controllers have CSAAT and friends. | ||
67 | * | 69 | * |
68 | * Since the CSAAT functionality is a bit weird on newer controllers | 70 | * Since the CSAAT functionality is a bit weird on newer controllers as |
69 | * as well, we use GPIO to control nCSx pins on all controllers. | 71 | * well, we use GPIO to control nCSx pins on all controllers, updating |
72 | * MR.PCS to avoid confusing the controller. Using GPIOs also lets us | ||
73 | * support active-high chipselects despite the controller's belief that | ||
74 | * only active-low devices/systems exists. | ||
75 | * | ||
76 | * However, at91rm9200 has a second erratum whereby nCS0 doesn't work | ||
77 | * right when driven with GPIO. ("Mode Fault does not allow more than one | ||
78 | * Master on Chip Select 0.") No workaround exists for that ... so for | ||
79 | * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, | ||
80 | * and (c) will trigger that first erratum in some cases. | ||
70 | */ | 81 | */ |
71 | 82 | ||
72 | static inline void cs_activate(struct spi_device *spi) | 83 | static void cs_activate(struct atmel_spi *as, struct spi_device *spi) |
73 | { | 84 | { |
74 | unsigned gpio = (unsigned) spi->controller_data; | 85 | unsigned gpio = (unsigned) spi->controller_data; |
75 | unsigned active = spi->mode & SPI_CS_HIGH; | 86 | unsigned active = spi->mode & SPI_CS_HIGH; |
87 | u32 mr; | ||
88 | |||
89 | mr = spi_readl(as, MR); | ||
90 | mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr); | ||
91 | |||
92 | dev_dbg(&spi->dev, "activate %u%s, mr %08x\n", | ||
93 | gpio, active ? " (high)" : "", | ||
94 | mr); | ||
76 | 95 | ||
77 | dev_dbg(&spi->dev, "activate %u%s\n", gpio, active ? " (high)" : ""); | 96 | if (!(cpu_is_at91rm9200() && spi->chip_select == 0)) |
78 | gpio_set_value(gpio, active); | 97 | gpio_set_value(gpio, active); |
98 | spi_writel(as, MR, mr); | ||
79 | } | 99 | } |
80 | 100 | ||
81 | static inline void cs_deactivate(struct spi_device *spi) | 101 | static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) |
82 | { | 102 | { |
83 | unsigned gpio = (unsigned) spi->controller_data; | 103 | unsigned gpio = (unsigned) spi->controller_data; |
84 | unsigned active = spi->mode & SPI_CS_HIGH; | 104 | unsigned active = spi->mode & SPI_CS_HIGH; |
105 | u32 mr; | ||
85 | 106 | ||
86 | dev_dbg(&spi->dev, "DEactivate %u%s\n", gpio, active ? " (low)" : ""); | 107 | /* only deactivate *this* device; sometimes transfers to |
87 | gpio_set_value(gpio, !active); | 108 | * another device may be active when this routine is called. |
109 | */ | ||
110 | mr = spi_readl(as, MR); | ||
111 | if (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select)) { | ||
112 | mr = SPI_BFINS(PCS, 0xf, mr); | ||
113 | spi_writel(as, MR, mr); | ||
114 | } | ||
115 | |||
116 | dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n", | ||
117 | gpio, active ? " (low)" : "", | ||
118 | mr); | ||
119 | |||
120 | if (!(cpu_is_at91rm9200() && spi->chip_select == 0)) | ||
121 | gpio_set_value(gpio, !active); | ||
88 | } | 122 | } |
89 | 123 | ||
90 | /* | 124 | /* |
@@ -140,6 +174,7 @@ static void atmel_spi_next_xfer(struct spi_master *master, | |||
140 | 174 | ||
141 | /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" | 175 | /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" |
142 | * mechanism might help avoid the IRQ latency between transfers | 176 | * mechanism might help avoid the IRQ latency between transfers |
177 | * (and improve the nCS0 errata handling on at91rm9200 chips) | ||
143 | * | 178 | * |
144 | * We're also waiting for ENDRX before we start the next | 179 | * We're also waiting for ENDRX before we start the next |
145 | * transfer because we need to handle some difficult timing | 180 | * transfer because we need to handle some difficult timing |
@@ -169,17 +204,25 @@ static void atmel_spi_next_message(struct spi_master *master) | |||
169 | { | 204 | { |
170 | struct atmel_spi *as = spi_master_get_devdata(master); | 205 | struct atmel_spi *as = spi_master_get_devdata(master); |
171 | struct spi_message *msg; | 206 | struct spi_message *msg; |
172 | u32 mr; | 207 | struct spi_device *spi; |
173 | 208 | ||
174 | BUG_ON(as->current_transfer); | 209 | BUG_ON(as->current_transfer); |
175 | 210 | ||
176 | msg = list_entry(as->queue.next, struct spi_message, queue); | 211 | msg = list_entry(as->queue.next, struct spi_message, queue); |
212 | spi = msg->spi; | ||
177 | 213 | ||
178 | /* Select the chip */ | 214 | dev_dbg(master->cdev.dev, "start message %p for %s\n", |
179 | mr = spi_readl(as, MR); | 215 | msg, spi->dev.bus_id); |
180 | mr = SPI_BFINS(PCS, ~(1 << msg->spi->chip_select), mr); | 216 | |
181 | spi_writel(as, MR, mr); | 217 | /* select chip if it's not still active */ |
182 | cs_activate(msg->spi); | 218 | if (as->stay) { |
219 | if (as->stay != spi) { | ||
220 | cs_deactivate(as, as->stay); | ||
221 | cs_activate(as, spi); | ||
222 | } | ||
223 | as->stay = NULL; | ||
224 | } else | ||
225 | cs_activate(as, spi); | ||
183 | 226 | ||
184 | atmel_spi_next_xfer(master, msg); | 227 | atmel_spi_next_xfer(master, msg); |
185 | } | 228 | } |
@@ -232,9 +275,13 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master, | |||
232 | 275 | ||
233 | static void | 276 | static void |
234 | atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, | 277 | atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, |
235 | struct spi_message *msg, int status) | 278 | struct spi_message *msg, int status, int stay) |
236 | { | 279 | { |
237 | cs_deactivate(msg->spi); | 280 | if (!stay || status < 0) |
281 | cs_deactivate(as, msg->spi); | ||
282 | else | ||
283 | as->stay = msg->spi; | ||
284 | |||
238 | list_del(&msg->queue); | 285 | list_del(&msg->queue); |
239 | msg->status = status; | 286 | msg->status = status; |
240 | 287 | ||
@@ -324,7 +371,7 @@ atmel_spi_interrupt(int irq, void *dev_id) | |||
324 | /* Clear any overrun happening while cleaning up */ | 371 | /* Clear any overrun happening while cleaning up */ |
325 | spi_readl(as, SR); | 372 | spi_readl(as, SR); |
326 | 373 | ||
327 | atmel_spi_msg_done(master, as, msg, -EIO); | 374 | atmel_spi_msg_done(master, as, msg, -EIO, 0); |
328 | } else if (pending & SPI_BIT(ENDRX)) { | 375 | } else if (pending & SPI_BIT(ENDRX)) { |
329 | ret = IRQ_HANDLED; | 376 | ret = IRQ_HANDLED; |
330 | 377 | ||
@@ -342,12 +389,13 @@ atmel_spi_interrupt(int irq, void *dev_id) | |||
342 | 389 | ||
343 | if (msg->transfers.prev == &xfer->transfer_list) { | 390 | if (msg->transfers.prev == &xfer->transfer_list) { |
344 | /* report completed message */ | 391 | /* report completed message */ |
345 | atmel_spi_msg_done(master, as, msg, 0); | 392 | atmel_spi_msg_done(master, as, msg, 0, |
393 | xfer->cs_change); | ||
346 | } else { | 394 | } else { |
347 | if (xfer->cs_change) { | 395 | if (xfer->cs_change) { |
348 | cs_deactivate(msg->spi); | 396 | cs_deactivate(as, msg->spi); |
349 | udelay(1); | 397 | udelay(1); |
350 | cs_activate(msg->spi); | 398 | cs_activate(as, msg->spi); |
351 | } | 399 | } |
352 | 400 | ||
353 | /* | 401 | /* |
@@ -410,6 +458,14 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
410 | return -EINVAL; | 458 | return -EINVAL; |
411 | } | 459 | } |
412 | 460 | ||
461 | /* see notes above re chipselect */ | ||
462 | if (cpu_is_at91rm9200() | ||
463 | && spi->chip_select == 0 | ||
464 | && (spi->mode & SPI_CS_HIGH)) { | ||
465 | dev_dbg(&spi->dev, "setup: can't be active-high\n"); | ||
466 | return -EINVAL; | ||
467 | } | ||
468 | |||
413 | /* speed zero convention is used by some upper layers */ | 469 | /* speed zero convention is used by some upper layers */ |
414 | bus_hz = clk_get_rate(as->clk); | 470 | bus_hz = clk_get_rate(as->clk); |
415 | if (spi->max_speed_hz) { | 471 | if (spi->max_speed_hz) { |
@@ -446,6 +502,14 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
446 | return ret; | 502 | return ret; |
447 | spi->controller_state = (void *)npcs_pin; | 503 | spi->controller_state = (void *)npcs_pin; |
448 | gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); | 504 | gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); |
505 | } else { | ||
506 | unsigned long flags; | ||
507 | |||
508 | spin_lock_irqsave(&as->lock, flags); | ||
509 | if (as->stay == spi) | ||
510 | as->stay = NULL; | ||
511 | cs_deactivate(as, spi); | ||
512 | spin_unlock_irqrestore(&as->lock, flags); | ||
449 | } | 513 | } |
450 | 514 | ||
451 | dev_dbg(&spi->dev, | 515 | dev_dbg(&spi->dev, |
@@ -502,6 +566,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) | |||
502 | } | 566 | } |
503 | } | 567 | } |
504 | 568 | ||
569 | #ifdef VERBOSE | ||
505 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | 570 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
506 | dev_dbg(controller, | 571 | dev_dbg(controller, |
507 | " xfer %p: len %u tx %p/%08x rx %p/%08x\n", | 572 | " xfer %p: len %u tx %p/%08x rx %p/%08x\n", |
@@ -509,6 +574,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) | |||
509 | xfer->tx_buf, xfer->tx_dma, | 574 | xfer->tx_buf, xfer->tx_dma, |
510 | xfer->rx_buf, xfer->rx_dma); | 575 | xfer->rx_buf, xfer->rx_dma); |
511 | } | 576 | } |
577 | #endif | ||
512 | 578 | ||
513 | msg->status = -EINPROGRESS; | 579 | msg->status = -EINPROGRESS; |
514 | msg->actual_length = 0; | 580 | msg->actual_length = 0; |
@@ -524,8 +590,21 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) | |||
524 | 590 | ||
525 | static void atmel_spi_cleanup(struct spi_device *spi) | 591 | static void atmel_spi_cleanup(struct spi_device *spi) |
526 | { | 592 | { |
527 | if (spi->controller_state) | 593 | struct atmel_spi *as = spi_master_get_devdata(spi->master); |
528 | gpio_free((unsigned int)spi->controller_data); | 594 | unsigned gpio = (unsigned) spi->controller_data; |
595 | unsigned long flags; | ||
596 | |||
597 | if (!spi->controller_state) | ||
598 | return; | ||
599 | |||
600 | spin_lock_irqsave(&as->lock, flags); | ||
601 | if (as->stay == spi) { | ||
602 | as->stay = NULL; | ||
603 | cs_deactivate(as, spi); | ||
604 | } | ||
605 | spin_unlock_irqrestore(&as->lock, flags); | ||
606 | |||
607 | gpio_free(gpio); | ||
529 | } | 608 | } |
530 | 609 | ||
531 | /*-------------------------------------------------------------------------*/ | 610 | /*-------------------------------------------------------------------------*/ |