Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 350783
b: refs/heads/master
c: b17de07
h: refs/heads/master
i:
  350781: f704fb4
  350779: f5ae225
  350775: 296046c
  350767: 334d07b
  350751: 1d9fb30
  350719: 15e90ca
v: v3
  • Loading branch information
Jonas Gorski authored and Grant Likely committed Feb 5, 2013
1 parent 72918d5 commit 0062aa9
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 29 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 32310aaf5c410dd3dc701556cc9bbeff0847c7c9
refs/heads/master: b17de076062a677b11ec5054d61184d0134e851f
134 changes: 106 additions & 28 deletions trunk/drivers/spi/spi-bcm63xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

#define PFX KBUILD_MODNAME

#define BCM63XX_SPI_MAX_PREPEND 15

struct bcm63xx_spi {
struct completion done;

Expand Down Expand Up @@ -169,33 +171,63 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
return 0;
}

static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
unsigned int num_transfers)
{
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
u16 msg_ctl;
u16 cmd;
u8 rx_tail;
unsigned int timeout = 0;
unsigned int i, timeout = 0, prepend_len = 0, len = 0;
struct spi_transfer *t = first;
bool do_rx = false;
bool do_tx = false;

/* Disable the CMD_DONE interrupt */
bcm_spi_writeb(bs, 0, SPI_INT_MASK);

dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
t->tx_buf, t->rx_buf, t->len);

if (t->tx_buf)
memcpy_toio(bs->tx_io, t->tx_buf, t->len);
if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
prepend_len = t->len;

/* prepare the buffer */
for (i = 0; i < num_transfers; i++) {
if (t->tx_buf) {
do_tx = true;
memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);

/* don't prepend more than one tx */
if (t != first)
prepend_len = 0;
}

if (t->rx_buf) {
do_rx = true;
/* prepend is half-duplex write only */
if (t == first)
prepend_len = 0;
}

len += t->len;

t = list_entry(t->transfer_list.next, struct spi_transfer,
transfer_list);
}

len -= prepend_len;

init_completion(&bs->done);

/* Fill in the Message control register */
msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
msg_ctl = (len << SPI_BYTE_CNT_SHIFT);

if (t->rx_buf && t->tx_buf)
if (do_rx && do_tx && prepend_len == 0)
msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
else if (t->rx_buf)
else if (do_rx)
msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
else if (t->tx_buf)
else if (do_tx)
msg_ctl |= (SPI_HD_W << bs->msg_type_shift);

switch (bs->msg_ctl_width) {
Expand All @@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)

/* Issue the transfer */
cmd = SPI_CMD_START_IMMEDIATE;
cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
bcm_spi_writew(bs, cmd, SPI_CMD);

Expand All @@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
/* read out all data */
rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);

if (do_rx && rx_tail != len)
return -EIO;

if (!rx_tail)
return 0;

len = 0;
t = first;
/* Read out all the data */
if (rx_tail)
memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail);
for (i = 0; i < num_transfers; i++) {
if (t->rx_buf)
memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);

if (t != first || prepend_len == 0)
len += t->len;

t = list_entry(t->transfer_list.next, struct spi_transfer,
transfer_list);
}

return 0;
}
Expand All @@ -252,46 +300,76 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
struct spi_message *m)
{
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
struct spi_transfer *t;
struct spi_transfer *t, *first = NULL;
struct spi_device *spi = m->spi;
int status = 0;

unsigned int n_transfers = 0, total_len = 0;
bool can_use_prepend = false;

/*
* This SPI controller does not support keeping CS active after a
* transfer.
* Work around this by merging as many transfers we can into one big
* full-duplex transfers.
*/
list_for_each_entry(t, &m->transfers, transfer_list) {
status = bcm63xx_spi_check_transfer(spi, t);
if (status < 0)
goto exit;

if (!first)
first = t;

n_transfers++;
total_len += t->len;

if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
first->len <= BCM63XX_SPI_MAX_PREPEND)
can_use_prepend = true;
else if (can_use_prepend && t->tx_buf)
can_use_prepend = false;

/* we can only transfer one fifo worth of data */
if (t->len > bs->fifo_size) {
if ((can_use_prepend &&
total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
(!can_use_prepend && total_len > bs->fifo_size)) {
dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
t->len, bs->fifo_size);
total_len, bs->fifo_size);
status = -EINVAL;
goto exit;
}

/* CS will be deasserted directly after transfer */
if (t->delay_usecs) {
dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
/* all combined transfers have to have the same speed */
if (t->speed_hz != first->speed_hz) {
dev_err(&spi->dev, "unable to change speed between transfers\n");
status = -EINVAL;
goto exit;
}

if (!t->cs_change &&
!list_is_last(&t->transfer_list, &m->transfers)) {
dev_err(&spi->dev, "unable to keep CS asserted between transfers\n");
/* CS will be deasserted directly after transfer */
if (t->delay_usecs) {
dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
status = -EINVAL;
goto exit;
}

/* configure adapter for a new transfer */
bcm63xx_spi_setup_transfer(spi, t);
if (t->cs_change ||
list_is_last(&t->transfer_list, &m->transfers)) {
/* configure adapter for a new transfer */
bcm63xx_spi_setup_transfer(spi, first);

/* send the data */
status = bcm63xx_txrx_bufs(spi, t);
if (status)
goto exit;
/* send the data */
status = bcm63xx_txrx_bufs(spi, first, n_transfers);
if (status)
goto exit;

m->actual_length += total_len;

m->actual_length += t->len;
first = NULL;
n_transfers = 0;
total_len = 0;
can_use_prepend = false;
}
}
exit:
m->status = status;
Expand Down

0 comments on commit 0062aa9

Please sign in to comment.