mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-08 07:35:04 +00:00
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>
This commit is contained in:
parent
9f87d6f26b
commit
d3b72c7e6b
1 changed files with 55 additions and 37 deletions
|
@ -694,6 +694,54 @@ static void atmel_spi_next_xfer_data(struct spi_master *master,
|
||||||
*plen = len;
|
*plen = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int atmel_spi_set_xfer_speed(struct atmel_spi *as,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
u32 scbr, csr;
|
||||||
|
unsigned long bus_hz;
|
||||||
|
|
||||||
|
/* v1 chips start out at half the peripheral bus speed. */
|
||||||
|
bus_hz = clk_get_rate(as->clk);
|
||||||
|
if (!atmel_spi_is_v2(as))
|
||||||
|
bus_hz /= 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the lowest divider that satisfies the
|
||||||
|
* constraint, assuming div32/fdiv/mbz == 0.
|
||||||
|
*/
|
||||||
|
if (xfer->speed_hz)
|
||||||
|
scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
|
||||||
|
else
|
||||||
|
/*
|
||||||
|
* This can happend if max_speed is null.
|
||||||
|
* In this case, we set the lowest possible speed
|
||||||
|
*/
|
||||||
|
scbr = 0xff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the resulting divider doesn't fit into the
|
||||||
|
* register bitfield, we can't satisfy the constraint.
|
||||||
|
*/
|
||||||
|
if (scbr >= (1 << SPI_SCBR_SIZE)) {
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"setup: %d Hz too slow, scbr %u; min %ld Hz\n",
|
||||||
|
xfer->speed_hz, scbr, bus_hz/255);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (scbr == 0) {
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"setup: %d Hz too high, scbr %u; max %ld Hz\n",
|
||||||
|
xfer->speed_hz, scbr, bus_hz);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
csr = spi_readl(as, CSR0 + 4 * spi->chip_select);
|
||||||
|
csr = SPI_BFINS(SCBR, scbr, csr);
|
||||||
|
spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submit next transfer for PDC.
|
* Submit next transfer for PDC.
|
||||||
* lock is held, spi irq is blocked
|
* lock is held, spi irq is blocked
|
||||||
|
@ -731,6 +779,8 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master,
|
||||||
spi_writel(as, RCR, len);
|
spi_writel(as, RCR, len);
|
||||||
spi_writel(as, TCR, len);
|
spi_writel(as, TCR, len);
|
||||||
|
|
||||||
|
atmel_spi_set_xfer_speed(as, msg->spi, xfer);
|
||||||
|
|
||||||
dev_dbg(&msg->spi->dev,
|
dev_dbg(&msg->spi->dev,
|
||||||
" start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
|
" start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
|
||||||
xfer, xfer->len, xfer->tx_buf,
|
xfer, xfer->len, xfer->tx_buf,
|
||||||
|
@ -823,6 +873,7 @@ static void atmel_spi_dma_next_xfer(struct spi_master *master,
|
||||||
|
|
||||||
as->current_transfer = xfer;
|
as->current_transfer = xfer;
|
||||||
len = xfer->len;
|
len = xfer->len;
|
||||||
|
atmel_spi_set_xfer_speed(as, msg->spi, xfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (atmel_spi_use_dma(as, xfer)) {
|
if (atmel_spi_use_dma(as, xfer)) {
|
||||||
|
@ -1264,9 +1315,8 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct atmel_spi *as;
|
struct atmel_spi *as;
|
||||||
struct atmel_spi_device *asd;
|
struct atmel_spi_device *asd;
|
||||||
u32 scbr, csr;
|
u32 csr;
|
||||||
unsigned int bits = spi->bits_per_word;
|
unsigned int bits = spi->bits_per_word;
|
||||||
unsigned long bus_hz;
|
|
||||||
unsigned int npcs_pin;
|
unsigned int npcs_pin;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1290,33 +1340,7 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* v1 chips start out at half the peripheral bus speed. */
|
csr = SPI_BF(BITS, bits - 8);
|
||||||
bus_hz = clk_get_rate(as->clk);
|
|
||||||
if (!atmel_spi_is_v2(as))
|
|
||||||
bus_hz /= 2;
|
|
||||||
|
|
||||||
if (spi->max_speed_hz) {
|
|
||||||
/*
|
|
||||||
* Calculate the lowest divider that satisfies the
|
|
||||||
* constraint, assuming div32/fdiv/mbz == 0.
|
|
||||||
*/
|
|
||||||
scbr = DIV_ROUND_UP(bus_hz, spi->max_speed_hz);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the resulting divider doesn't fit into the
|
|
||||||
* register bitfield, we can't satisfy the constraint.
|
|
||||||
*/
|
|
||||||
if (scbr >= (1 << SPI_SCBR_SIZE)) {
|
|
||||||
dev_dbg(&spi->dev,
|
|
||||||
"setup: %d Hz too slow, scbr %u; min %ld Hz\n",
|
|
||||||
spi->max_speed_hz, scbr, bus_hz/255);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
/* speed zero means "as slow as possible" */
|
|
||||||
scbr = 0xff;
|
|
||||||
|
|
||||||
csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8);
|
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
csr |= SPI_BIT(CPOL);
|
csr |= SPI_BIT(CPOL);
|
||||||
if (!(spi->mode & SPI_CPHA))
|
if (!(spi->mode & SPI_CPHA))
|
||||||
|
@ -1363,8 +1387,8 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
asd->csr = csr;
|
asd->csr = csr;
|
||||||
|
|
||||||
dev_dbg(&spi->dev,
|
dev_dbg(&spi->dev,
|
||||||
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
|
"setup: bpw %u mode 0x%x -> csr%d %08x\n",
|
||||||
bus_hz / scbr, bits, spi->mode, spi->chip_select, csr);
|
bits, spi->mode, spi->chip_select, csr);
|
||||||
|
|
||||||
if (!atmel_spi_is_v2(as))
|
if (!atmel_spi_is_v2(as))
|
||||||
spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME implement these protocol options!! */
|
|
||||||
if (xfer->speed_hz < spi->max_speed_hz) {
|
|
||||||
dev_dbg(&spi->dev, "can't change speed in transfer\n");
|
|
||||||
return -ENOPROTOOPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DMA map early, for performance (empties dcache ASAP) and
|
* DMA map early, for performance (empties dcache ASAP) and
|
||||||
* better fault reporting.
|
* better fault reporting.
|
||||||
|
|
Loading…
Add table
Reference in a new issue