/* * WAIT state * * A transfer has completed; need to wait for the delay period to complete * before starting the next transfer */ static int mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) { if (status && irq) dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n", status); if (((int)get_tbl()) - ms->timestamp < 0) return FSM_POLL; ms->message->actual_length += ms->transfer->len; /* Check if there is another transfer in this message. If there * aren't then deactivate CS, notify sender, and drop back to idle * to start the next message. */ if (ms->transfer->transfer_list.next == &ms->message->transfers) { ms->msg_count++; mpc52xx_spi_chipsel(ms, 0); ms->message->status = 0; ms->message->complete(ms->message->context); ms->state = mpc52xx_spi_fsmstate_idle; return FSM_CONTINUE; } /* There is another transfer; kick it off */ if (ms->cs_change) mpc52xx_spi_chipsel(ms, 0); ms->transfer = container_of(ms->transfer->transfer_list.next, struct spi_transfer, transfer_list); mpc52xx_spi_start_transfer(ms); ms->state = mpc52xx_spi_fsmstate_transfer; return FSM_CONTINUE; }
/* * TRANSFER state * * In the middle of a transfer. If the SPI core has completed processing * a byte, then read out the received data and write out the next byte * (unless this transfer is finished; in which case go on to the wait * state) */ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) { if (!status) return ms->irq0 ? FSM_STOP : FSM_POLL; if (status & SPI_STATUS_WCOL) { /* The SPI controller is stoopid. At slower speeds, it may * raise the SPIF flag before the state machine is actually * finished, which causes a collision (internal to the state * machine only). The manual recommends inserting a delay * between receiving the interrupt and sending the next byte, * but it can also be worked around simply by retrying the * transfer which is what we do here. */ ms->wcol_count++; ms->wcol_ticks += get_tbl() - ms->wcol_tx_timestamp; ms->wcol_tx_timestamp = get_tbl(); data = 0; if (ms->tx_buf) data = *(ms->tx_buf - 1); out_8(ms->regs + SPI_DATA, data); /* try again */ return FSM_CONTINUE; } else if (status & SPI_STATUS_MODF) { ms->modf_count++; dev_err(&ms->master->dev, "mode fault\n"); mpc52xx_spi_chipsel(ms, 0); ms->message->status = -EIO; if (ms->message->complete) ms->message->complete(ms->message->context); ms->state = mpc52xx_spi_fsmstate_idle; return FSM_CONTINUE; } /* Read data out of the spi device */ ms->byte_count++; if (ms->rx_buf) *ms->rx_buf++ = data; /* Is the transfer complete? */ ms->len--; if (ms->len == 0) { ms->timestamp = get_tbl(); ms->timestamp += ms->transfer->delay_usecs * tb_ticks_per_usec; ms->state = mpc52xx_spi_fsmstate_wait; return FSM_CONTINUE; } /* Write out the next byte */ ms->wcol_tx_timestamp = get_tbl(); if (ms->tx_buf) out_8(ms->regs + SPI_DATA, *ms->tx_buf++); else out_8(ms->regs + SPI_DATA, 0); return FSM_CONTINUE; }
/* * Start a new transfer. This is called both by the idle state * for the first transfer in a message, and by the wait state when the * previous transfer in a message is complete. */ static void mpc52xx_spi_start_transfer(struct mpc52xx_spi *ms) { ms->rx_buf = ms->transfer->rx_buf; ms->tx_buf = ms->transfer->tx_buf; ms->len = ms->transfer->len; /* Activate the chip select */ if (ms->cs_change) mpc52xx_spi_chipsel(ms, 1); ms->cs_change = ms->transfer->cs_change; /* Write out the first byte */ ms->wcol_tx_timestamp = get_tbl(); if (ms->tx_buf) out_8(ms->regs + SPI_DATA, *ms->tx_buf++); else out_8(ms->regs + SPI_DATA, 0); }