diff options
author | Richard Genoud <richard.genoud@gmail.com> | 2013-11-07 04:34:06 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-01-08 08:00:24 -0500 |
commit | d3b72c7e6bf33185a5de1db2164ff237759c554c (patch) | |
tree | 3424f0cf9ddffe2a236bb1da8a4182e990fc7013 /drivers/spi/spi-atmel.c | |
parent | 9f87d6f26b2fcedfc3d1ec6c65ce568b21546ee2 (diff) |
spi: atmel: add support for changing message transfer speed
The only speed available was max_speed (the maximum speed declared for a
device).
This patch adds the support for spi_tranfer->speed_hz parameter.
We can now set a different speed for each spi message.
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'drivers/spi/spi-atmel.c')
-rw-r--r-- | drivers/spi/spi-atmel.c | 92 |
1 files changed, 55 insertions, 37 deletions
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 57fa73876223..b96f9a89cdc6 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c | |||
@@ -694,6 +694,54 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, | |||
694 | *plen = len; | 694 | *plen = len; |
695 | } | 695 | } |
696 | 696 | ||
697 | static int atmel_spi_set_xfer_speed(struct atmel_spi *as, | ||
698 | struct spi_device *spi, | ||
699 | struct spi_transfer *xfer) | ||
700 | { | ||
701 | u32 scbr, csr; | ||
702 | unsigned long bus_hz; | ||
703 | |||
704 | /* v1 chips start out at half the peripheral bus speed. */ | ||
705 | bus_hz = clk_get_rate(as->clk); | ||
706 | if (!atmel_spi_is_v2(as)) | ||
707 | bus_hz /= 2; | ||
708 | |||
709 | /* | ||
710 | * Calculate the lowest divider that satisfies the | ||
711 | * constraint, assuming div32/fdiv/mbz == 0. | ||
712 | */ | ||
713 | if (xfer->speed_hz) | ||
714 | scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); | ||
715 | else | ||
716 | /* | ||
717 | * This can happend if max_speed is null. | ||
718 | * In this case, we set the lowest possible speed | ||
719 | */ | ||
720 | scbr = 0xff; | ||
721 | |||
722 | /* | ||
723 | * If the resulting divider doesn't fit into the | ||
724 | * register bitfield, we can't satisfy the constraint. | ||
725 | */ | ||
726 | if (scbr >= (1 << SPI_SCBR_SIZE)) { | ||
727 | dev_err(&spi->dev, | ||
728 | "setup: %d Hz too slow, scbr %u; min %ld Hz\n", | ||
729 | xfer->speed_hz, scbr, bus_hz/255); | ||
730 | return -EINVAL; | ||
731 | } | ||
732 | if (scbr == 0) { | ||
733 | dev_err(&spi->dev, | ||
734 | "setup: %d Hz too high, scbr %u; max %ld Hz\n", | ||
735 | xfer->speed_hz, scbr, bus_hz); | ||
736 | return -EINVAL; | ||
737 | } | ||
738 | csr = spi_readl(as, CSR0 + 4 * spi->chip_select); | ||
739 | csr = SPI_BFINS(SCBR, scbr, csr); | ||
740 | spi_writel(as, CSR0 + 4 * spi->chip_select, csr); | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
697 | /* | 745 | /* |
698 | * Submit next transfer for PDC. | 746 | * Submit next transfer for PDC. |
699 | * lock is held, spi irq is blocked | 747 | * lock is held, spi irq is blocked |
@@ -731,6 +779,8 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, | |||
731 | spi_writel(as, RCR, len); | 779 | spi_writel(as, RCR, len); |
732 | spi_writel(as, TCR, len); | 780 | spi_writel(as, TCR, len); |
733 | 781 | ||
782 | atmel_spi_set_xfer_speed(as, msg->spi, xfer); | ||
783 | |||
734 | dev_dbg(&msg->spi->dev, | 784 | dev_dbg(&msg->spi->dev, |
735 | " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", | 785 | " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", |
736 | xfer, xfer->len, xfer->tx_buf, | 786 | xfer, xfer->len, xfer->tx_buf, |
@@ -823,6 +873,7 @@ static void atmel_spi_dma_next_xfer(struct spi_master *master, | |||
823 | 873 | ||
824 | as->current_transfer = xfer; | 874 | as->current_transfer = xfer; |
825 | len = xfer->len; | 875 | len = xfer->len; |
876 | atmel_spi_set_xfer_speed(as, msg->spi, xfer); | ||
826 | } | 877 | } |
827 | 878 | ||
828 | if (atmel_spi_use_dma(as, xfer)) { | 879 | if (atmel_spi_use_dma(as, xfer)) { |
@@ -1264,9 +1315,8 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
1264 | { | 1315 | { |
1265 | struct atmel_spi *as; | 1316 | struct atmel_spi *as; |
1266 | struct atmel_spi_device *asd; | 1317 | struct atmel_spi_device *asd; |
1267 | u32 scbr, csr; | 1318 | u32 csr; |
1268 | unsigned int bits = spi->bits_per_word; | 1319 | unsigned int bits = spi->bits_per_word; |
1269 | unsigned long bus_hz; | ||
1270 | unsigned int npcs_pin; | 1320 | unsigned int npcs_pin; |
1271 | int ret; | 1321 | int ret; |
1272 | 1322 | ||
@@ -1290,33 +1340,7 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
1290 | return -EINVAL; | 1340 | return -EINVAL; |
1291 | } | 1341 | } |
1292 | 1342 | ||
1293 | /* v1 chips start out at half the peripheral bus speed. */ | 1343 | csr = SPI_BF(BITS, bits - 8); |
1294 | bus_hz = clk_get_rate(as->clk); | ||
1295 | if (!atmel_spi_is_v2(as)) | ||
1296 | bus_hz /= 2; | ||
1297 | |||
1298 | if (spi->max_speed_hz) { | ||
1299 | /* | ||
1300 | * Calculate the lowest divider that satisfies the | ||
1301 | * constraint, assuming div32/fdiv/mbz == 0. | ||
1302 | */ | ||
1303 | scbr = DIV_ROUND_UP(bus_hz, spi->max_speed_hz); | ||
1304 | |||
1305 | /* | ||
1306 | * If the resulting divider doesn't fit into the | ||
1307 | * register bitfield, we can't satisfy the constraint. | ||
1308 | */ | ||
1309 | if (scbr >= (1 << SPI_SCBR_SIZE)) { | ||
1310 | dev_dbg(&spi->dev, | ||
1311 | "setup: %d Hz too slow, scbr %u; min %ld Hz\n", | ||
1312 | spi->max_speed_hz, scbr, bus_hz/255); | ||
1313 | return -EINVAL; | ||
1314 | } | ||
1315 | } else | ||
1316 | /* speed zero means "as slow as possible" */ | ||
1317 | scbr = 0xff; | ||
1318 | |||
1319 | csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8); | ||
1320 | if (spi->mode & SPI_CPOL) | 1344 | if (spi->mode & SPI_CPOL) |
1321 | csr |= SPI_BIT(CPOL); | 1345 | csr |= SPI_BIT(CPOL); |
1322 | if (!(spi->mode & SPI_CPHA)) | 1346 | if (!(spi->mode & SPI_CPHA)) |
@@ -1363,8 +1387,8 @@ static int atmel_spi_setup(struct spi_device *spi) | |||
1363 | asd->csr = csr; | 1387 | asd->csr = csr; |
1364 | 1388 | ||
1365 | dev_dbg(&spi->dev, | 1389 | dev_dbg(&spi->dev, |
1366 | "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", | 1390 | "setup: bpw %u mode 0x%x -> csr%d %08x\n", |
1367 | bus_hz / scbr, bits, spi->mode, spi->chip_select, csr); | 1391 | bits, spi->mode, spi->chip_select, csr); |
1368 | 1392 | ||
1369 | if (!atmel_spi_is_v2(as)) | 1393 | if (!atmel_spi_is_v2(as)) |
1370 | spi_writel(as, CSR0 + 4 * spi->chip_select, csr); | 1394 | spi_writel(as, CSR0 + 4 * spi->chip_select, csr); |
@@ -1414,12 +1438,6 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) | |||
1414 | } | 1438 | } |
1415 | } | 1439 | } |
1416 | 1440 | ||
1417 | /* FIXME implement these protocol options!! */ | ||
1418 | if (xfer->speed_hz < spi->max_speed_hz) { | ||
1419 | dev_dbg(&spi->dev, "can't change speed in transfer\n"); | ||
1420 | return -ENOPROTOOPT; | ||
1421 | } | ||
1422 | |||
1423 | /* | 1441 | /* |
1424 | * DMA map early, for performance (empties dcache ASAP) and | 1442 | * DMA map early, for performance (empties dcache ASAP) and |
1425 | * better fault reporting. | 1443 | * better fault reporting. |