Loading drivers/spi/spi-s3c64xx.c +99 −108 Original line number Diff line number Diff line Loading @@ -381,7 +381,7 @@ static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, #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; Loading @@ -407,7 +407,7 @@ 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, desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, dma->direction, DMA_PREP_INTERRUPT); desc->callback = s3c64xx_spi_dmacb; Loading Loading @@ -515,7 +515,11 @@ 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; #ifndef CONFIG_S3C_DMA prepare_dma(&sdd->tx_dma, &xfer->tx_sg); #else prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); #endif } else { switch (sdd->cur_bpw) { case 32: Loading Loading @@ -547,7 +551,11 @@ 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); #ifndef CONFIG_S3C_DMA prepare_dma(&sdd->rx_dma, &xfer->rx_sg); #else prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); #endif } } Loading @@ -555,23 +563,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) { Loading @@ -593,30 +584,20 @@ 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 { 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 Loading @@ -642,10 +623,30 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, /* If timed out while checking rx/tx status return error */ if (!val) return -EIO; } else { 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; /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 10; /* some tolerance */ val = msecs_to_loops(ms); do { status = readl(regs + S3C64XX_SPI_STATUS); } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); /* If it was only Tx */ if (!xfer->rx_buf) { Loading Loading @@ -686,21 +687,10 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, buf = buf + cpy_len; } while (loops--); sdd->state &= ~RXBUSY; } return 0; } static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { if (sdd->tgl_spi == spi) sdd->tgl_spi = NULL; if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) { void __iomem *regs = sdd->regs; Loading Loading @@ -929,7 +919,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", Loading Loading @@ -1092,14 +1085,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); Loading drivers/spi/spi.c +180 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/cache.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/of_irq.h> Loading Loading @@ -578,6 +580,169 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->master->set_cs(spi, !enable); } static int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, enum dma_data_direction dir) { const bool vmalloced_buf = is_vmalloc_addr(buf); const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len; const int sgs = DIV_ROUND_UP(len, desc_len); struct page *vm_page; void *sg_buf; size_t min; int i, ret; ret = sg_alloc_table(sgt, sgs, GFP_KERNEL); if (ret != 0) return ret; for (i = 0; i < sgs; i++) { min = min_t(size_t, len, desc_len); if (vmalloced_buf) { vm_page = vmalloc_to_page(buf); if (!vm_page) { sg_free_table(sgt); return -ENOMEM; } sg_buf = page_address(vm_page) + ((size_t)buf & ~PAGE_MASK); } else { sg_buf = buf; } sg_set_buf(&sgt->sgl[i], sg_buf, min); buf += min; len -= min; } ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); if (ret < 0) { sg_free_table(sgt); return ret; } sgt->nents = ret; return 0; } static void spi_unmap_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, enum dma_data_direction dir) { if (sgt->orig_nents) { dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); sg_free_table(sgt); } } static int spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct device *tx_dev, *rx_dev; struct spi_transfer *xfer; void *tmp; unsigned int max_tx, max_rx; int ret; if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { max_tx = 0; max_rx = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if ((master->flags & SPI_MASTER_MUST_TX) && !xfer->tx_buf) max_tx = max(xfer->len, max_tx); if ((master->flags & SPI_MASTER_MUST_RX) && !xfer->rx_buf) max_rx = max(xfer->len, max_rx); } if (max_tx) { tmp = krealloc(master->dummy_tx, max_tx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_tx = tmp; memset(tmp, 0, max_tx); } if (max_rx) { tmp = krealloc(master->dummy_rx, max_rx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_rx = tmp; } if (max_tx || max_rx) { list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf) xfer->tx_buf = master->dummy_tx; if (!xfer->rx_buf) xfer->rx_buf = master->dummy_rx; } } } if (!master->can_dma) return 0; tx_dev = &master->dma_tx->dev->device; rx_dev = &master->dma_rx->dev->device; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; if (xfer->tx_buf != NULL) { ret = spi_map_buf(master, tx_dev, &xfer->tx_sg, (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); if (ret != 0) return ret; } if (xfer->rx_buf != NULL) { ret = spi_map_buf(master, rx_dev, &xfer->rx_sg, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); if (ret != 0) { spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); return ret; } } } master->cur_msg_mapped = true; return 0; } static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; struct device *tx_dev, *rx_dev; if (!master->cur_msg_mapped || !master->can_dma) return 0; tx_dev = &master->dma_tx->dev->device; rx_dev = &master->dma_rx->dev->device; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; spi_unmap_buf(master, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); } return 0; } /* * spi_transfer_one_message - Default implementation of transfer_one_message() * Loading Loading @@ -684,6 +849,10 @@ static void spi_pump_messages(struct kthread_work *work) } master->busy = false; spin_unlock_irqrestore(&master->queue_lock, flags); kfree(master->dummy_rx); master->dummy_rx = NULL; kfree(master->dummy_tx); master->dummy_tx = NULL; if (master->unprepare_transfer_hardware && master->unprepare_transfer_hardware(master)) dev_err(&master->dev, Loading Loading @@ -750,6 +919,13 @@ static void spi_pump_messages(struct kthread_work *work) master->cur_msg_prepared = true; } ret = spi_map_msg(master, master->cur_msg); if (ret) { master->cur_msg->status = ret; spi_finalize_current_message(master); return; } ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, Loading Loading @@ -837,6 +1013,8 @@ void spi_finalize_current_message(struct spi_master *master) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); spi_unmap_msg(master, mesg); if (master->cur_msg_prepared && master->unprepare_message) { ret = master->unprepare_message(master, mesg); if (ret) { Loading Loading @@ -1370,6 +1548,8 @@ int spi_register_master(struct spi_master *master) mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); if (!master->max_dma_len) master->max_dma_len = INT_MAX; /* register the device, then userspace will see it. * registration fails if the bus ID is in use. Loading include/linux/spi/spi.h +31 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/completion.h> #include <linux/scatterlist.h> struct dma_chan; /* * INTERFACES between SPI master-side drivers and SPI infrastructure. Loading Loading @@ -266,6 +269,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @auto_runtime_pm: the core should ensure a runtime PM reference is held * while the hardware is prepared, using the parent * device for the spidev * @max_dma_len: Maximum length of a DMA transfer for the device. * @prepare_transfer_hardware: a message will soon arrive from the queue * so the subsystem requests the driver to prepare the transfer hardware * by issuing this call Loading Loading @@ -348,6 +352,8 @@ struct spi_master { #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; Loading Loading @@ -389,6 +395,17 @@ struct spi_master { /* called on release() to free memory provided by spi_master */ void (*cleanup)(struct spi_device *spi); /* * Used to enable core support for DMA handling, if can_dma() * exists and returns true then the transfer will be mapped * prior to transfer_one() being called. The driver should * not modify or store xfer and dma_tx and dma_rx must be set * while the device is prepared. */ bool (*can_dma)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); /* * These hooks are for drivers that want to use the generic * master transfer queueing mechanism. If these are used, the Loading @@ -407,7 +424,9 @@ struct spi_master { bool rt; bool auto_runtime_pm; bool cur_msg_prepared; bool cur_msg_mapped; struct completion xfer_completion; size_t max_dma_len; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, Loading @@ -428,6 +447,14 @@ struct spi_master { /* gpio chip select */ int *cs_gpios; /* DMA channels for use with core dmaengine helpers */ struct dma_chan *dma_tx; struct dma_chan *dma_rx; /* dummy data for full duplex devices */ void *dummy_rx; void *dummy_tx; }; static inline void *spi_master_get_devdata(struct spi_master *master) Loading Loading @@ -512,6 +539,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. * @transfer_list: transfers are sequenced through @spi_message.transfers * @tx_sg: Scatterlist for transmit, currently not for client use * @rx_sg: Scatterlist for receive, currently not for client use * * SPI transfers always write the same number of bytes as they read. * Protocol drivers should always provide @rx_buf and/or @tx_buf. Loading Loading @@ -579,6 +608,8 @@ struct spi_transfer { dma_addr_t tx_dma; dma_addr_t rx_dma; struct sg_table tx_sg; struct sg_table rx_sg; unsigned cs_change:1; unsigned tx_nbits:3; Loading Loading
drivers/spi/spi-s3c64xx.c +99 −108 Original line number Diff line number Diff line Loading @@ -381,7 +381,7 @@ static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, #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; Loading @@ -407,7 +407,7 @@ 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, desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, dma->direction, DMA_PREP_INTERRUPT); desc->callback = s3c64xx_spi_dmacb; Loading Loading @@ -515,7 +515,11 @@ 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; #ifndef CONFIG_S3C_DMA prepare_dma(&sdd->tx_dma, &xfer->tx_sg); #else prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); #endif } else { switch (sdd->cur_bpw) { case 32: Loading Loading @@ -547,7 +551,11 @@ 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); #ifndef CONFIG_S3C_DMA prepare_dma(&sdd->rx_dma, &xfer->rx_sg); #else prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); #endif } } Loading @@ -555,23 +563,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) { Loading @@ -593,30 +584,20 @@ 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 { 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 Loading @@ -642,10 +623,30 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, /* If timed out while checking rx/tx status return error */ if (!val) return -EIO; } else { 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; /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 10; /* some tolerance */ val = msecs_to_loops(ms); do { status = readl(regs + S3C64XX_SPI_STATUS); } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); /* If it was only Tx */ if (!xfer->rx_buf) { Loading Loading @@ -686,21 +687,10 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, buf = buf + cpy_len; } while (loops--); sdd->state &= ~RXBUSY; } return 0; } static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { if (sdd->tgl_spi == spi) sdd->tgl_spi = NULL; if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) { void __iomem *regs = sdd->regs; Loading Loading @@ -929,7 +919,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", Loading Loading @@ -1092,14 +1085,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); Loading
drivers/spi/spi.c +180 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/cache.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/of_irq.h> Loading Loading @@ -578,6 +580,169 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->master->set_cs(spi, !enable); } static int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, enum dma_data_direction dir) { const bool vmalloced_buf = is_vmalloc_addr(buf); const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len; const int sgs = DIV_ROUND_UP(len, desc_len); struct page *vm_page; void *sg_buf; size_t min; int i, ret; ret = sg_alloc_table(sgt, sgs, GFP_KERNEL); if (ret != 0) return ret; for (i = 0; i < sgs; i++) { min = min_t(size_t, len, desc_len); if (vmalloced_buf) { vm_page = vmalloc_to_page(buf); if (!vm_page) { sg_free_table(sgt); return -ENOMEM; } sg_buf = page_address(vm_page) + ((size_t)buf & ~PAGE_MASK); } else { sg_buf = buf; } sg_set_buf(&sgt->sgl[i], sg_buf, min); buf += min; len -= min; } ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); if (ret < 0) { sg_free_table(sgt); return ret; } sgt->nents = ret; return 0; } static void spi_unmap_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, enum dma_data_direction dir) { if (sgt->orig_nents) { dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); sg_free_table(sgt); } } static int spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct device *tx_dev, *rx_dev; struct spi_transfer *xfer; void *tmp; unsigned int max_tx, max_rx; int ret; if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { max_tx = 0; max_rx = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if ((master->flags & SPI_MASTER_MUST_TX) && !xfer->tx_buf) max_tx = max(xfer->len, max_tx); if ((master->flags & SPI_MASTER_MUST_RX) && !xfer->rx_buf) max_rx = max(xfer->len, max_rx); } if (max_tx) { tmp = krealloc(master->dummy_tx, max_tx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_tx = tmp; memset(tmp, 0, max_tx); } if (max_rx) { tmp = krealloc(master->dummy_rx, max_rx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_rx = tmp; } if (max_tx || max_rx) { list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf) xfer->tx_buf = master->dummy_tx; if (!xfer->rx_buf) xfer->rx_buf = master->dummy_rx; } } } if (!master->can_dma) return 0; tx_dev = &master->dma_tx->dev->device; rx_dev = &master->dma_rx->dev->device; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; if (xfer->tx_buf != NULL) { ret = spi_map_buf(master, tx_dev, &xfer->tx_sg, (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); if (ret != 0) return ret; } if (xfer->rx_buf != NULL) { ret = spi_map_buf(master, rx_dev, &xfer->rx_sg, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); if (ret != 0) { spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); return ret; } } } master->cur_msg_mapped = true; return 0; } static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; struct device *tx_dev, *rx_dev; if (!master->cur_msg_mapped || !master->can_dma) return 0; tx_dev = &master->dma_tx->dev->device; rx_dev = &master->dma_rx->dev->device; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; spi_unmap_buf(master, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); } return 0; } /* * spi_transfer_one_message - Default implementation of transfer_one_message() * Loading Loading @@ -684,6 +849,10 @@ static void spi_pump_messages(struct kthread_work *work) } master->busy = false; spin_unlock_irqrestore(&master->queue_lock, flags); kfree(master->dummy_rx); master->dummy_rx = NULL; kfree(master->dummy_tx); master->dummy_tx = NULL; if (master->unprepare_transfer_hardware && master->unprepare_transfer_hardware(master)) dev_err(&master->dev, Loading Loading @@ -750,6 +919,13 @@ static void spi_pump_messages(struct kthread_work *work) master->cur_msg_prepared = true; } ret = spi_map_msg(master, master->cur_msg); if (ret) { master->cur_msg->status = ret; spi_finalize_current_message(master); return; } ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, Loading Loading @@ -837,6 +1013,8 @@ void spi_finalize_current_message(struct spi_master *master) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); spi_unmap_msg(master, mesg); if (master->cur_msg_prepared && master->unprepare_message) { ret = master->unprepare_message(master, mesg); if (ret) { Loading Loading @@ -1370,6 +1548,8 @@ int spi_register_master(struct spi_master *master) mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); if (!master->max_dma_len) master->max_dma_len = INT_MAX; /* register the device, then userspace will see it. * registration fails if the bus ID is in use. Loading
include/linux/spi/spi.h +31 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/completion.h> #include <linux/scatterlist.h> struct dma_chan; /* * INTERFACES between SPI master-side drivers and SPI infrastructure. Loading Loading @@ -266,6 +269,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @auto_runtime_pm: the core should ensure a runtime PM reference is held * while the hardware is prepared, using the parent * device for the spidev * @max_dma_len: Maximum length of a DMA transfer for the device. * @prepare_transfer_hardware: a message will soon arrive from the queue * so the subsystem requests the driver to prepare the transfer hardware * by issuing this call Loading Loading @@ -348,6 +352,8 @@ struct spi_master { #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; Loading Loading @@ -389,6 +395,17 @@ struct spi_master { /* called on release() to free memory provided by spi_master */ void (*cleanup)(struct spi_device *spi); /* * Used to enable core support for DMA handling, if can_dma() * exists and returns true then the transfer will be mapped * prior to transfer_one() being called. The driver should * not modify or store xfer and dma_tx and dma_rx must be set * while the device is prepared. */ bool (*can_dma)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); /* * These hooks are for drivers that want to use the generic * master transfer queueing mechanism. If these are used, the Loading @@ -407,7 +424,9 @@ struct spi_master { bool rt; bool auto_runtime_pm; bool cur_msg_prepared; bool cur_msg_mapped; struct completion xfer_completion; size_t max_dma_len; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, Loading @@ -428,6 +447,14 @@ struct spi_master { /* gpio chip select */ int *cs_gpios; /* DMA channels for use with core dmaengine helpers */ struct dma_chan *dma_tx; struct dma_chan *dma_rx; /* dummy data for full duplex devices */ void *dummy_rx; void *dummy_tx; }; static inline void *spi_master_get_devdata(struct spi_master *master) Loading Loading @@ -512,6 +539,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. * @transfer_list: transfers are sequenced through @spi_message.transfers * @tx_sg: Scatterlist for transmit, currently not for client use * @rx_sg: Scatterlist for receive, currently not for client use * * SPI transfers always write the same number of bytes as they read. * Protocol drivers should always provide @rx_buf and/or @tx_buf. Loading Loading @@ -579,6 +608,8 @@ struct spi_transfer { dma_addr_t tx_dma; dma_addr_t rx_dma; struct sg_table tx_sg; struct sg_table rx_sg; unsigned cs_change:1; unsigned tx_nbits:3; Loading