commit: http://blackfin.uclinux.org/git/?p=linux-kernel;a=commitdiff;h=ec8e10730438f5fba6f5498382bc1bb935fd01b9 branch: http://blackfin.uclinux.org/git/?p=linux-kernel;a=shortlog;h=refs/heads/trunk
Signed-off-by: Scott Jiang <[email protected]> --- drivers/spi/spi-bfin6xx.c | 423 +++++++++++++++++++++------------------------ 1 files changed, 200 insertions(+), 223 deletions(-) diff --git a/drivers/spi/spi-bfin6xx.c b/drivers/spi/spi-bfin6xx.c index e6e2546..a44c66b 100644 --- a/drivers/spi/spi-bfin6xx.c +++ b/drivers/spi/spi-bfin6xx.c @@ -67,7 +67,6 @@ struct bfin_spi_master { struct spi_transfer *cur_transfer; struct bfin_spi_device *cur_chip; unsigned transfer_len; - unsigned cs_change; /* transfer buffer */ void *tx; @@ -351,11 +350,14 @@ static void bfin_spi_next_transfer(struct bfin_spi_master *drv) struct spi_transfer *t = drv->cur_transfer; /* Move to next transfer */ - if (t->transfer_list.next != &msg->transfers) - t = list_entry(t->transfer_list.next, + if (t->transfer_list.next != &msg->transfers) { + drv->cur_transfer = list_entry(t->transfer_list.next, struct spi_transfer, transfer_list); - else - t = NULL; + drv->state = RUNNING_STATE; + } else { + drv->state = DONE_STATE; + drv->cur_transfer = NULL; + } } static void bfin_spi_giveback(struct bfin_spi_master *drv_data) @@ -366,247 +368,233 @@ static void bfin_spi_giveback(struct bfin_spi_master *drv_data) spi_finalize_current_message(drv_data->master); } -static void bfin_spi_pump_transfers(unsigned long data) +static int bfin_spi_setup_transfer(struct bfin_spi_master *drv) { - struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; - struct spi_message *message = NULL; - struct spi_transfer *transfer = NULL; - struct spi_transfer *previous = NULL; - struct bfin_spi_device *chip = NULL; - unsigned int bits_per_word; + struct spi_transfer *t = drv->cur_transfer; u32 cr, cr_width; - bool tranf_success = true; - bool full_duplex = false; - - /* Get current state information */ - message = drv_data->cur_msg; - transfer = drv_data->cur_transfer; - chip = drv_data->cur_chip; - /* Handle for abort */ - if (drv_data->state == ERROR_STATE) { - message->status = -EIO; - bfin_spi_giveback(drv_data); - return; - } - - /* Handle end of message */ - if (drv_data->state == DONE_STATE) { - message->status = 0; - bfin_spi_flush(drv_data); - bfin_spi_giveback(drv_data); - return; - } - - /* Delay if requested at end of transfer */ - if (drv_data->state == RUNNING_STATE) { - previous = list_entry(transfer->transfer_list.prev, - struct spi_transfer, transfer_list); - if (previous->delay_usecs) - udelay(previous->delay_usecs); - } - - /* Flush any existing transfers that may be sitting in the hardware */ - if (bfin_spi_flush(drv_data) == 0) { - message->status = -EIO; - bfin_spi_giveback(drv_data); - return; - } - - if ((transfer->len == 0) || (transfer->tx_buf == NULL - && transfer->rx_buf == NULL)) { - /* Move to next transfer of this msg */ - bfin_spi_next_transfer(drv_data); - /* Schedule next transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - return; - } - - if (transfer->tx_buf != NULL) { - drv_data->tx = (void *)transfer->tx_buf; - drv_data->tx_end = drv_data->tx + transfer->len; + if (t->tx_buf) { + drv->tx = (void *)t->tx_buf; + drv->tx_end = drv->tx + t->len; } else { - drv_data->tx = NULL; + drv->tx = NULL; } - if (transfer->rx_buf != NULL) { - full_duplex = (transfer->tx_buf != NULL) ? true : false; - drv_data->rx = transfer->rx_buf; - drv_data->rx_end = drv_data->rx + transfer->len; + if (t->rx_buf) { + drv->rx = t->rx_buf; + drv->rx_end = drv->rx + t->len; } else { - drv_data->rx = NULL; + drv->rx = NULL; } - drv_data->transfer_len = transfer->len; - drv_data->cs_change = transfer->cs_change; + drv->transfer_len = t->len; - /* Bits per word setup */ - bits_per_word = transfer->bits_per_word ? : - message->spi->bits_per_word ? : 8; - switch (bits_per_word) { + /* bits per word setup */ + switch (t->bits_per_word) { case 8: cr_width = SPI_CTL_SIZE08; - drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; + drv->ops = &bfin_bfin_spi_transfer_ops_u8; break; case 16: cr_width = SPI_CTL_SIZE16; - drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; + drv->ops = &bfin_bfin_spi_transfer_ops_u16; break; case 32: cr_width = SPI_CTL_SIZE32; - drv_data->ops = &bfin_bfin_spi_transfer_ops_u32; + drv->ops = &bfin_bfin_spi_transfer_ops_u32; break; default: - message->status = -EINVAL; - bfin_spi_giveback(drv_data); - return; + return -EINVAL; } - cr = bfin_read(&drv_data->regs->control) & ~SPI_CTL_SIZE; + cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE; cr |= cr_width; - bfin_write(&drv_data->regs->control, cr); - - drv_data->state = RUNNING_STATE; - - /* Speed setup (surely valid because already checked) */ - if (transfer->speed_hz) - bfin_write(&drv_data->regs->clock, - hz_to_spi_clock(drv_data->sclk, transfer->speed_hz)); - else - bfin_write(&drv_data->regs->clock, chip->clock); + bfin_write(&drv->regs->control, cr); - bfin_write(&drv_data->regs->status, 0xFFFFFFFF); - bfin_spi_cs_active(drv_data, chip); + /* speed setup */ + bfin_write(&drv->regs->clock, + hz_to_spi_clock(drv->sclk, t->speed_hz)); + return 0; +} - if (chip->enable_dma) { - u32 dma_config; - unsigned long word_count, word_size; - void *tx_buf, *rx_buf; - - switch (bits_per_word) { - case 8: - dma_config = WDSIZE_8 | PSIZE_8; - word_count = drv_data->transfer_len; - word_size = 1; - break; - case 16: - dma_config = WDSIZE_16 | PSIZE_16; - word_count = drv_data->transfer_len / 2; - word_size = 2; - break; - default: - dma_config = WDSIZE_32 | PSIZE_32; - word_count = drv_data->transfer_len / 4; - word_size = 4; - break; - } +static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_transfer *t = drv_data->cur_transfer; + struct spi_message *msg = drv_data->cur_msg; + struct bfin_spi_device *chip = drv_data->cur_chip; + u32 dma_config; + unsigned long word_count, word_size; + void *tx_buf, *rx_buf; - if (full_duplex) { - WARN_ON((drv_data->tx_end - drv_data->tx) - != (drv_data->rx_end - drv_data->rx)); - tx_buf = drv_data->tx; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = drv_data->rx_dma_size - = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, word_size); - } else if (drv_data->tx) { - tx_buf = drv_data->tx; - rx_buf = &drv_data->dummy_buffer; - drv_data->tx_dma_size = drv_data->transfer_len; - drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); - set_dma_x_modify(drv_data->tx_dma, word_size); - set_dma_x_modify(drv_data->rx_dma, 0); - } else { - drv_data->dummy_buffer = chip->tx_dummy_val; - tx_buf = &drv_data->dummy_buffer; - rx_buf = drv_data->rx; - drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); - drv_data->rx_dma_size = drv_data->transfer_len; - set_dma_x_modify(drv_data->tx_dma, 0); - set_dma_x_modify(drv_data->rx_dma, word_size); - } + switch (t->bits_per_word) { + case 8: + dma_config = WDSIZE_8 | PSIZE_8; + word_count = drv_data->transfer_len; + word_size = 1; + break; + case 16: + dma_config = WDSIZE_16 | PSIZE_16; + word_count = drv_data->transfer_len / 2; + word_size = 2; + break; + default: + dma_config = WDSIZE_32 | PSIZE_32; + word_count = drv_data->transfer_len / 4; + word_size = 4; + break; + } - drv_data->tx_dma_addr = dma_map_single(&message->spi->dev, - (void *)tx_buf, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - if (dma_mapping_error(&message->spi->dev, - drv_data->tx_dma_addr)) { - drv_data->state = ERROR_STATE; - return; - } + if (!drv_data->rx) { + tx_buf = drv_data->tx; + rx_buf = &drv_data->dummy_buffer; + drv_data->tx_dma_size = drv_data->transfer_len; + drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, 0); + } else if (!drv_data->tx) { + drv_data->dummy_buffer = chip->tx_dummy_val; + tx_buf = &drv_data->dummy_buffer; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); + drv_data->rx_dma_size = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, 0); + set_dma_x_modify(drv_data->rx_dma, word_size); + } else { + tx_buf = drv_data->tx; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = drv_data->rx_dma_size + = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, word_size); + } + + drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)tx_buf, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->tx_dma_addr)) + return -ENOMEM; - drv_data->rx_dma_addr = dma_map_single(&message->spi->dev, - (void *)rx_buf, - drv_data->rx_dma_size, - DMA_FROM_DEVICE); - if (dma_mapping_error(&message->spi->dev, - drv_data->rx_dma_addr)) { - drv_data->state = ERROR_STATE; - dma_unmap_single(&message->spi->dev, - drv_data->tx_dma_addr, - drv_data->tx_dma_size, - DMA_TO_DEVICE); - return; - } + drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)rx_buf, + drv_data->rx_dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->rx_dma_addr)) { + dma_unmap_single(&msg->spi->dev, + drv_data->tx_dma_addr, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + return -ENOMEM; + } - dummy_read(drv_data); - set_dma_x_count(drv_data->tx_dma, word_count); - set_dma_x_count(drv_data->rx_dma, word_count); - set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); - set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); - dma_config |= DMAFLOW_STOP | RESTART | DI_EN; - set_dma_config(drv_data->tx_dma, dma_config); - set_dma_config(drv_data->rx_dma, dma_config | WNR); - enable_dma(drv_data->tx_dma); - enable_dma(drv_data->rx_dma); - SSYNC(); - - bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); - SSYNC(); - bfin_write(&drv_data->regs->tx_control, - SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); + dummy_read(drv_data); + set_dma_x_count(drv_data->tx_dma, word_count); + set_dma_x_count(drv_data->rx_dma, word_count); + set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); + set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); + dma_config |= DMAFLOW_STOP | RESTART | DI_EN; + set_dma_config(drv_data->tx_dma, dma_config); + set_dma_config(drv_data->rx_dma, dma_config | WNR); + enable_dma(drv_data->tx_dma); + enable_dma(drv_data->rx_dma); + SSYNC(); - return; - } + bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); + SSYNC(); + bfin_write(&drv_data->regs->tx_control, + SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); - if (full_duplex) { - /* full duplex mode */ - WARN_ON((drv_data->tx_end - drv_data->tx) - != (drv_data->rx_end - drv_data->rx)); + return 0; +} - drv_data->ops->duplex(drv_data); +static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; - if (drv_data->tx != drv_data->tx_end) - tranf_success = false; - } else if (drv_data->tx != NULL) { + if (!drv_data->rx) { /* write only half duplex */ drv_data->ops->write(drv_data); - if (drv_data->tx != drv_data->tx_end) - tranf_success = false; - } else { + return -EIO; + } else if (!drv_data->tx) { /* read only half duplex */ drv_data->ops->read(drv_data); if (drv_data->rx != drv_data->rx_end) - tranf_success = false; + return -EIO; + } else { + /* full duplex mode */ + drv_data->ops->duplex(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; } - if (!tranf_success) { - drv_data->state = ERROR_STATE; - } else { - /* Update total byte transferred */ - message->actual_length += drv_data->transfer_len; - /* Move to next transfer of this msg */ - bfin_spi_next_transfer(drv_data); - if (drv_data->cs_change) { - bfin_spi_flush(drv_data); + if (!bfin_spi_flush(drv_data)) + return -EIO; + msg->actual_length += drv_data->transfer_len; + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +static void bfin_spi_pump_transfers(unsigned long data) +{ + struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; + struct spi_message *msg = NULL; + struct spi_transfer *t = NULL; + struct bfin_spi_device *chip = NULL; + int ret; + + /* Get current state information */ + msg = drv_data->cur_msg; + t = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + msg->status = -EIO; + bfin_spi_giveback(drv_data); + return; + } + + if (drv_data->state == RUNNING_STATE) { + if (t->delay_usecs) + udelay(t->delay_usecs); + if (t->cs_change) bfin_spi_cs_deactive(drv_data, chip); - } + bfin_spi_next_transfer(drv_data); + t = drv_data->cur_transfer; + } + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + msg->status = 0; + bfin_spi_giveback(drv_data); + return; } - /* Schedule next transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); + if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return; + } + + ret = bfin_spi_setup_transfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } + + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + bfin_spi_cs_active(drv_data, chip); + drv_data->state = RUNNING_STATE; + + if (chip->enable_dma) + ret = bfin_spi_dma_xfer(drv_data); + else + ret = bfin_spi_pio_xfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } } static int bfin_spi_transfer_one_message(struct spi_master *master, @@ -649,9 +637,12 @@ static int bfin_spi_setup(struct spi_device *spi) u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; int ret = -EINVAL; - if (spi->bits_per_word != 8 - && spi->bits_per_word != 16 - && spi->bits_per_word != 32) { + switch (spi->bits_per_word) { + case 8: + case 16: + case 32: + break; + default: dev_err(&spi->dev, "%d bits_per_word is not supported\n", spi->bits_per_word); return -EINVAL; @@ -667,8 +658,8 @@ static int bfin_spi_setup(struct spi_device *spi) } if (chip_info) { if (chip_info->control & ~bfin_ctl_reg) { - dev_err(&spi->dev, "do not set bits " - "that the SPI framework manages\n"); + dev_err(&spi->dev, + "do not set bits that the SPI framework manages\n"); goto error; } chip->control = chip_info->control; @@ -700,11 +691,6 @@ static int bfin_spi_setup(struct spi_device *spi) /* force a default base state */ chip->control &= bfin_ctl_reg; - /* translate common spi framework into our register */ - if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) { - dev_err(&spi->dev, "unsupported spi modes detected\n"); - goto pin_error; - } if (spi->mode & SPI_CPOL) chip->control |= SPI_CTL_CPOL; if (spi->mode & SPI_CPHA) @@ -721,11 +707,6 @@ static int bfin_spi_setup(struct spi_device *spi) bfin_spi_cs_deactive(drv_data, chip); return 0; -pin_error: - if (chip->cs < MAX_CTRL_CS) - peripheral_free(ssel[spi->master->bus_num][chip->cs - 1]); - else - gpio_free(chip->cs_gpio); error: if (chip) { kfree(chip); @@ -775,7 +756,6 @@ static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id) static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) { struct bfin_spi_master *drv_data = dev_id; - struct bfin_spi_device *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); @@ -785,9 +765,6 @@ static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) /* we may fail on tx dma */ if (drv_data->state != ERROR_STATE) msg->actual_length += drv_data->transfer_len; - if (drv_data->cs_change) - bfin_spi_cs_deactive(drv_data, chip); - bfin_spi_next_transfer(drv_data); } else { drv_data->state = ERROR_STATE; dev_err(&drv_data->master->dev, @@ -968,6 +945,7 @@ static int bfin_spi_resume(struct device *dev) struct bfin_spi_master *drv_data = spi_master_get_devdata(master); int ret = 0; + /* bootrom may modify spi and dma status when resume in spi boot mode */ ret = request_dma(drv_data->tx_dma, "SPI_TX_DMA"); if (ret) { dev_err(dev, "cannot request SPI TX DMA channel\n"); @@ -981,7 +959,6 @@ static int bfin_spi_resume(struct device *dev) free_dma(drv_data->tx_dma); return ret; } - /* rx dma is enabled when resume in spi boot mode */ disable_dma(drv_data->rx_dma); set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data);
_______________________________________________ Linux-kernel-commits mailing list [email protected] https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits
