diff options
Diffstat (limited to 'drivers/spi/spi-s3c64xx.c')
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 424 |
1 files changed, 105 insertions, 319 deletions
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ae907dde1371..f19cd97855e8 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -34,10 +34,6 @@ #include <linux/platform_data/spi-s3c64xx.h> -#ifdef CONFIG_S3C_DMA -#include <mach/dma.h> -#endif - #define MAX_SPI_PORTS 3 #define S3C64XX_SPI_QUIRK_POLL (1 << 0) @@ -200,9 +196,6 @@ struct s3c64xx_spi_driver_data { unsigned cur_speed; struct s3c64xx_spi_dma_data rx_dma; struct s3c64xx_spi_dma_data tx_dma; -#ifdef CONFIG_S3C_DMA - struct samsung_dma_ops *ops; -#endif struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; bool cs_gpio; @@ -284,104 +277,8 @@ static void s3c64xx_spi_dmacb(void *data) spin_unlock_irqrestore(&sdd->lock, flags); } -#ifdef CONFIG_S3C_DMA -/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */ - -static struct s3c2410_dma_client s3c64xx_spi_dma_client = { - .name = "samsung-spi-dma", -}; - -static void prepare_dma(struct s3c64xx_spi_dma_data *dma, - unsigned len, dma_addr_t buf) -{ - struct s3c64xx_spi_driver_data *sdd; - struct samsung_dma_prep info; - struct samsung_dma_config config; - - if (dma->direction == DMA_DEV_TO_MEM) { - sdd = container_of((void *)dma, - struct s3c64xx_spi_driver_data, rx_dma); - config.direction = sdd->rx_dma.direction; - config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; - config.width = sdd->cur_bpw / 8; - sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config); - } else { - sdd = container_of((void *)dma, - struct s3c64xx_spi_driver_data, tx_dma); - config.direction = sdd->tx_dma.direction; - config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; - config.width = sdd->cur_bpw / 8; - sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config); - } - - info.cap = DMA_SLAVE; - info.len = len; - info.fp = s3c64xx_spi_dmacb; - info.fp_param = dma; - info.direction = dma->direction; - info.buf = buf; - - sdd->ops->prepare((enum dma_ch)dma->ch, &info); - sdd->ops->trigger((enum dma_ch)dma->ch); -} - -static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) -{ - struct samsung_dma_req req; - struct device *dev = &sdd->pdev->dev; - - sdd->ops = samsung_dma_get_ops(); - - req.cap = DMA_SLAVE; - req.client = &s3c64xx_spi_dma_client; - - sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( - sdd->rx_dma.dmach, &req, dev, "rx"); - sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( - sdd->tx_dma.dmach, &req, dev, "tx"); - - return 1; -} - -static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* - * If DMA resource was not available during - * probe, no need to continue with dma requests - * else Acquire DMA channels - */ - while (!is_polling(sdd) && !acquire_dma(sdd)) - usleep_range(10000, 11000); - - return 0; -} - -static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* Free DMA channels */ - if (!is_polling(sdd)) { - sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, - &s3c64xx_spi_dma_client); - sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, - &s3c64xx_spi_dma_client); - } - - return 0; -} - -static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, - struct s3c64xx_spi_dma_data *dma) -{ - sdd->ops->stop((enum dma_ch)dma->ch); -} -#else - static void prepare_dma(struct s3c64xx_spi_dma_data *dma, - unsigned len, dma_addr_t buf) + struct sg_table *sgt) { struct s3c64xx_spi_driver_data *sdd; struct dma_slave_config config; @@ -407,8 +304,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, dmaengine_slave_config(dma->ch, &config); } - desc = dmaengine_prep_slave_single(dma->ch, buf, len, - dma->direction, DMA_PREP_INTERRUPT); + desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, + dma->direction, DMA_PREP_INTERRUPT); desc->callback = s3c64xx_spi_dmacb; desc->callback_param = dma; @@ -437,6 +334,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) ret = -EBUSY; goto out; } + spi->dma_rx = sdd->rx_dma.ch; sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, (void *)sdd->tx_dma.dmach, dev, "tx"); @@ -445,6 +343,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) ret = -EBUSY; goto out_rx; } + spi->dma_tx = sdd->tx_dma.ch; } ret = pm_runtime_get_sync(&sdd->pdev->dev); @@ -477,12 +376,14 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) return 0; } -static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, - struct s3c64xx_spi_dma_data *dma) +static bool s3c64xx_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { - dmaengine_terminate_all(dma->ch); + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + + return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; } -#endif static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi, @@ -515,7 +416,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); + prepare_dma(&sdd->tx_dma, &xfer->tx_sg); } else { switch (sdd->cur_bpw) { case 32: @@ -547,7 +448,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); + prepare_dma(&sdd->rx_dma, &xfer->rx_sg); } } @@ -555,23 +456,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(chcfg, regs + S3C64XX_SPI_CH_CFG); } -static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, - struct spi_device *spi) -{ - if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */ - if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ - /* Deselect the last toggled device */ - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, - spi->mode & SPI_CS_HIGH ? 0 : 1); - } - sdd->tgl_spi = NULL; - } - - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); -} - static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, int timeout_ms) { @@ -593,112 +477,111 @@ static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, return RX_FIFO_LVL(status, sdd); } -static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, - struct spi_transfer *xfer, int dma_mode) +static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd, + struct spi_transfer *xfer) { void __iomem *regs = sdd->regs; unsigned long val; + u32 status; int ms; /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 10; /* some tolerance */ - if (dma_mode) { - val = msecs_to_jiffies(ms) + 10; - val = wait_for_completion_timeout(&sdd->xfer_completion, val); - } else { - u32 status; - val = msecs_to_loops(ms); - do { + val = msecs_to_jiffies(ms) + 10; + val = wait_for_completion_timeout(&sdd->xfer_completion, val); + + /* + * If the previous xfer was completed within timeout, then + * proceed further else return -EIO. + * DmaTx returns after simply writing data in the FIFO, + * w/o waiting for real transmission on the bus to finish. + * DmaRx returns only after Dma read data from FIFO which + * needs bus transmission to finish, so we don't worry if + * Xfer involved Rx(with or without Tx). + */ + if (val && !xfer->rx_buf) { + val = msecs_to_loops(10); + status = readl(regs + S3C64XX_SPI_STATUS); + while ((TX_FIFO_LVL(status, sdd) + || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) + && --val) { + cpu_relax(); status = readl(regs + S3C64XX_SPI_STATUS); - } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); + } + } - if (dma_mode) { - u32 status; - - /* - * If the previous xfer was completed within timeout, then - * proceed further else return -EIO. - * DmaTx returns after simply writing data in the FIFO, - * w/o waiting for real transmission on the bus to finish. - * DmaRx returns only after Dma read data from FIFO which - * needs bus transmission to finish, so we don't worry if - * Xfer involved Rx(with or without Tx). - */ - if (val && !xfer->rx_buf) { - val = msecs_to_loops(10); - status = readl(regs + S3C64XX_SPI_STATUS); - while ((TX_FIFO_LVL(status, sdd) - || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) - && --val) { - cpu_relax(); - status = readl(regs + S3C64XX_SPI_STATUS); - } + /* If timed out while checking rx/tx status return error */ + if (!val) + return -EIO; - } + return 0; +} - /* If timed out while checking rx/tx status return error */ - if (!val) - return -EIO; - } else { - int loops; - u32 cpy_len; - u8 *buf; - - /* If it was only Tx */ - if (!xfer->rx_buf) { - sdd->state &= ~TXBUSY; - return 0; - } +static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd, + struct spi_transfer *xfer) +{ + void __iomem *regs = sdd->regs; + unsigned long val; + u32 status; + int loops; + u32 cpy_len; + u8 *buf; + int ms; - /* - * If the receive length is bigger than the controller fifo - * size, calculate the loops and read the fifo as many times. - * loops = length / max fifo size (calculated by using the - * fifo mask). - * For any size less than the fifo size the below code is - * executed atleast once. - */ - loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); - buf = xfer->rx_buf; - do { - /* wait for data to be received in the fifo */ - cpy_len = s3c64xx_spi_wait_for_timeout(sdd, - (loops ? ms : 0)); + /* millisecs to xfer 'len' bytes @ 'cur_speed' */ + ms = xfer->len * 8 * 1000 / sdd->cur_speed; + ms += 10; /* some tolerance */ - switch (sdd->cur_bpw) { - case 32: - ioread32_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len / 4); - break; - case 16: - ioread16_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len / 2); - break; - default: - ioread8_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len); - break; - } + val = msecs_to_loops(ms); + do { + status = readl(regs + S3C64XX_SPI_STATUS); + } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); - buf = buf + cpy_len; - } while (loops--); - sdd->state &= ~RXBUSY; + + /* If it was only Tx */ + if (!xfer->rx_buf) { + sdd->state &= ~TXBUSY; + return 0; } - return 0; -} + /* + * If the receive length is bigger than the controller fifo + * size, calculate the loops and read the fifo as many times. + * loops = length / max fifo size (calculated by using the + * fifo mask). + * For any size less than the fifo size the below code is + * executed atleast once. + */ + loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); + buf = xfer->rx_buf; + do { + /* wait for data to be received in the fifo */ + cpy_len = s3c64xx_spi_wait_for_timeout(sdd, + (loops ? ms : 0)); + + switch (sdd->cur_bpw) { + case 32: + ioread32_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 4); + break; + case 16: + ioread16_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 2); + break; + default: + ioread8_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len); + break; + } -static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, - struct spi_device *spi) -{ - if (sdd->tgl_spi == spi) - sdd->tgl_spi = NULL; + buf = buf + cpy_len; + } while (loops--); + sdd->state &= ~RXBUSY; - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); + return 0; } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) @@ -774,81 +657,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) -static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, - struct spi_message *msg) -{ - struct device *dev = &sdd->pdev->dev; - struct spi_transfer *xfer; - - if (is_polling(sdd) || msg->is_dma_mapped) - return 0; - - /* First mark all xfer unmapped */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - xfer->rx_dma = XFER_DMAADDR_INVALID; - xfer->tx_dma = XFER_DMAADDR_INVALID; - } - - /* Map until end or first fail */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - - if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1)) - continue; - - if (xfer->tx_buf != NULL) { - xfer->tx_dma = dma_map_single(dev, - (void *)xfer->tx_buf, xfer->len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, xfer->tx_dma)) { - dev_err(dev, "dma_map_single Tx failed\n"); - xfer->tx_dma = XFER_DMAADDR_INVALID; - return -ENOMEM; - } - } - - if (xfer->rx_buf != NULL) { - xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, - xfer->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, xfer->rx_dma)) { - dev_err(dev, "dma_map_single Rx failed\n"); - dma_unmap_single(dev, xfer->tx_dma, - xfer->len, DMA_TO_DEVICE); - xfer->tx_dma = XFER_DMAADDR_INVALID; - xfer->rx_dma = XFER_DMAADDR_INVALID; - return -ENOMEM; - } - } - } - - return 0; -} - -static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, - struct spi_message *msg) -{ - struct device *dev = &sdd->pdev->dev; - struct spi_transfer *xfer; - - if (is_polling(sdd) || msg->is_dma_mapped) - return; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - - if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1)) - continue; - - if (xfer->rx_buf != NULL - && xfer->rx_dma != XFER_DMAADDR_INVALID) - dma_unmap_single(dev, xfer->rx_dma, - xfer->len, DMA_FROM_DEVICE); - - if (xfer->tx_buf != NULL - && xfer->tx_dma != XFER_DMAADDR_INVALID) - dma_unmap_single(dev, xfer->tx_dma, - xfer->len, DMA_TO_DEVICE); - } -} - static int s3c64xx_spi_prepare_message(struct spi_master *master, struct spi_message *msg) { @@ -866,13 +674,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, s3c64xx_spi_config(sdd); } - /* Map all the transfers if needed */ - if (s3c64xx_spi_map_mssg(sdd, msg)) { - dev_err(&spi->dev, - "Xfer: Unable to map message buffers!\n"); - return -ENOMEM; - } - /* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); @@ -896,13 +697,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, bpw = xfer->bits_per_word; speed = xfer->speed_hz ? : spi->max_speed_hz; - if (xfer->len % (bpw / 8)) { - dev_err(&spi->dev, - "Xfer length(%u) not a multiple of word size(%u)\n", - xfer->len, bpw / 8); - return -EIO; - } - if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; @@ -929,7 +723,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, spin_unlock_irqrestore(&sdd->lock, flags); - status = wait_for_xfer(sdd, xfer, use_dma); + if (use_dma) + status = wait_for_dma(sdd, xfer); + else + status = wait_for_pio(sdd, xfer); if (status) { dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", @@ -941,10 +738,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma); + dmaengine_terminate_all(sdd->tx_dma.ch); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma); + dmaengine_terminate_all(sdd->rx_dma.ch); } } else { flush_fifo(sdd); @@ -953,16 +750,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, return status; } -static int s3c64xx_spi_unprepare_message(struct spi_master *master, - struct spi_message *msg) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - - s3c64xx_spi_unmap_mssg(sdd, msg); - - return 0; -} - static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( struct spi_device *spi) { @@ -1092,14 +879,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi) pm_runtime_put(&sdd->pdev->dev); writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - disable_cs(sdd, spi); return 0; setup_exit: pm_runtime_put(&sdd->pdev->dev); /* setup() returns with device de-selected */ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - disable_cs(sdd, spi); gpio_free(cs->line); spi_set_ctldata(spi, NULL); @@ -1338,7 +1123,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; master->prepare_message = s3c64xx_spi_prepare_message; master->transfer_one = s3c64xx_spi_transfer_one; - master->unprepare_message = s3c64xx_spi_unprepare_message; master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; @@ -1347,6 +1131,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->auto_runtime_pm = true; + if (!is_polling(sdd)) + master->can_dma = s3c64xx_spi_can_dma; sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sdd->regs)) { |