Merge remote-tracking branch 'spi/topic/dma' into spi-next
This commit is contained in:
@@ -381,7 +381,7 @@ static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
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 s3c64xx_spi_driver_data *sdd;
|
||||||
struct dma_slave_config config;
|
struct dma_slave_config config;
|
||||||
@@ -407,8 +407,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
|||||||
dmaengine_slave_config(dma->ch, &config);
|
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);
|
dma->direction, DMA_PREP_INTERRUPT);
|
||||||
|
|
||||||
desc->callback = s3c64xx_spi_dmacb;
|
desc->callback = s3c64xx_spi_dmacb;
|
||||||
desc->callback_param = dma;
|
desc->callback_param = dma;
|
||||||
@@ -515,7 +515,11 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
||||||
if (dma_mode) {
|
if (dma_mode) {
|
||||||
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
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);
|
prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
switch (sdd->cur_bpw) {
|
switch (sdd->cur_bpw) {
|
||||||
case 32:
|
case 32:
|
||||||
@@ -547,7 +551,11 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
||||||
| S3C64XX_SPI_PACKET_CNT_EN,
|
| S3C64XX_SPI_PACKET_CNT_EN,
|
||||||
regs + S3C64XX_SPI_PACKET_CNT);
|
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);
|
prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,23 +563,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
|
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,
|
static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
|
||||||
int timeout_ms)
|
int timeout_ms)
|
||||||
{
|
{
|
||||||
@@ -593,112 +584,111 @@ static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
|
|||||||
return RX_FIFO_LVL(status, sdd);
|
return RX_FIFO_LVL(status, sdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
|
static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_transfer *xfer, int dma_mode)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
void __iomem *regs = sdd->regs;
|
void __iomem *regs = sdd->regs;
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
u32 status;
|
||||||
int ms;
|
int ms;
|
||||||
|
|
||||||
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
||||||
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
|
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
|
||||||
ms += 10; /* some tolerance */
|
ms += 10; /* some tolerance */
|
||||||
|
|
||||||
if (dma_mode) {
|
val = msecs_to_jiffies(ms) + 10;
|
||||||
val = msecs_to_jiffies(ms) + 10;
|
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
|
||||||
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
|
|
||||||
} else {
|
/*
|
||||||
u32 status;
|
* If the previous xfer was completed within timeout, then
|
||||||
val = msecs_to_loops(ms);
|
* proceed further else return -EIO.
|
||||||
do {
|
* 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);
|
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;
|
|
||||||
} else {
|
|
||||||
int loops;
|
|
||||||
u32 cpy_len;
|
|
||||||
u8 *buf;
|
|
||||||
|
|
||||||
/* If it was only Tx */
|
|
||||||
if (!xfer->rx_buf) {
|
|
||||||
sdd->state &= ~TXBUSY;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf + cpy_len;
|
|
||||||
} while (loops--);
|
|
||||||
sdd->state &= ~RXBUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If timed out while checking rx/tx status return error */
|
||||||
|
if (!val)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
|
static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_device *spi)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
if (sdd->tgl_spi == spi)
|
void __iomem *regs = sdd->regs;
|
||||||
sdd->tgl_spi = NULL;
|
unsigned long val;
|
||||||
|
u32 status;
|
||||||
|
int loops;
|
||||||
|
u32 cpy_len;
|
||||||
|
u8 *buf;
|
||||||
|
int ms;
|
||||||
|
|
||||||
if (spi->cs_gpio >= 0)
|
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
||||||
gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
|
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) {
|
||||||
|
sdd->state &= ~TXBUSY;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf + cpy_len;
|
||||||
|
} while (loops--);
|
||||||
|
sdd->state &= ~RXBUSY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||||
@@ -929,7 +919,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
|||||||
|
|
||||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
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) {
|
if (status) {
|
||||||
dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
||||||
@@ -1092,14 +1085,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||||||
|
|
||||||
pm_runtime_put(&sdd->pdev->dev);
|
pm_runtime_put(&sdd->pdev->dev);
|
||||||
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
||||||
disable_cs(sdd, spi);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
setup_exit:
|
setup_exit:
|
||||||
pm_runtime_put(&sdd->pdev->dev);
|
pm_runtime_put(&sdd->pdev->dev);
|
||||||
/* setup() returns with device de-selected */
|
/* setup() returns with device de-selected */
|
||||||
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
||||||
disable_cs(sdd, spi);
|
|
||||||
|
|
||||||
gpio_free(cs->line);
|
gpio_free(cs->line);
|
||||||
spi_set_ctldata(spi, NULL);
|
spi_set_ctldata(spi, NULL);
|
||||||
|
@@ -24,6 +24,8 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/cache.h>
|
#include <linux/cache.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
@@ -578,6 +580,169 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
|||||||
spi->master->set_cs(spi, !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()
|
* spi_transfer_one_message - Default implementation of transfer_one_message()
|
||||||
*
|
*
|
||||||
@@ -684,6 +849,10 @@ static void spi_pump_messages(struct kthread_work *work)
|
|||||||
}
|
}
|
||||||
master->busy = false;
|
master->busy = false;
|
||||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
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 &&
|
if (master->unprepare_transfer_hardware &&
|
||||||
master->unprepare_transfer_hardware(master))
|
master->unprepare_transfer_hardware(master))
|
||||||
dev_err(&master->dev,
|
dev_err(&master->dev,
|
||||||
@@ -750,6 +919,13 @@ static void spi_pump_messages(struct kthread_work *work)
|
|||||||
master->cur_msg_prepared = true;
|
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);
|
ret = master->transfer_one_message(master, master->cur_msg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&master->dev,
|
dev_err(&master->dev,
|
||||||
@@ -837,6 +1013,8 @@ void spi_finalize_current_message(struct spi_master *master)
|
|||||||
queue_kthread_work(&master->kworker, &master->pump_messages);
|
queue_kthread_work(&master->kworker, &master->pump_messages);
|
||||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||||
|
|
||||||
|
spi_unmap_msg(master, mesg);
|
||||||
|
|
||||||
if (master->cur_msg_prepared && master->unprepare_message) {
|
if (master->cur_msg_prepared && master->unprepare_message) {
|
||||||
ret = master->unprepare_message(master, mesg);
|
ret = master->unprepare_message(master, mesg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -1370,6 +1548,8 @@ int spi_register_master(struct spi_master *master)
|
|||||||
mutex_init(&master->bus_lock_mutex);
|
mutex_init(&master->bus_lock_mutex);
|
||||||
master->bus_lock_flag = 0;
|
master->bus_lock_flag = 0;
|
||||||
init_completion(&master->xfer_completion);
|
init_completion(&master->xfer_completion);
|
||||||
|
if (!master->max_dma_len)
|
||||||
|
master->max_dma_len = INT_MAX;
|
||||||
|
|
||||||
/* register the device, then userspace will see it.
|
/* register the device, then userspace will see it.
|
||||||
* registration fails if the bus ID is in use.
|
* registration fails if the bus ID is in use.
|
||||||
|
@@ -24,6 +24,9 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
|
struct dma_chan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
||||||
@@ -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
|
* @auto_runtime_pm: the core should ensure a runtime PM reference is held
|
||||||
* while the hardware is prepared, using the parent
|
* while the hardware is prepared, using the parent
|
||||||
* device for the spidev
|
* 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
|
* @prepare_transfer_hardware: a message will soon arrive from the queue
|
||||||
* so the subsystem requests the driver to prepare the transfer hardware
|
* so the subsystem requests the driver to prepare the transfer hardware
|
||||||
* by issuing this call
|
* by issuing this call
|
||||||
@@ -348,6 +352,8 @@ struct spi_master {
|
|||||||
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
#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_RX BIT(1) /* can't do buffer read */
|
||||||
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
|
#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 */
|
/* lock and mutex for SPI bus locking */
|
||||||
spinlock_t bus_lock_spinlock;
|
spinlock_t bus_lock_spinlock;
|
||||||
@@ -389,6 +395,17 @@ struct spi_master {
|
|||||||
/* called on release() to free memory provided by spi_master */
|
/* called on release() to free memory provided by spi_master */
|
||||||
void (*cleanup)(struct spi_device *spi);
|
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
|
* These hooks are for drivers that want to use the generic
|
||||||
* master transfer queueing mechanism. If these are used, the
|
* master transfer queueing mechanism. If these are used, the
|
||||||
@@ -407,7 +424,9 @@ struct spi_master {
|
|||||||
bool rt;
|
bool rt;
|
||||||
bool auto_runtime_pm;
|
bool auto_runtime_pm;
|
||||||
bool cur_msg_prepared;
|
bool cur_msg_prepared;
|
||||||
|
bool cur_msg_mapped;
|
||||||
struct completion xfer_completion;
|
struct completion xfer_completion;
|
||||||
|
size_t max_dma_len;
|
||||||
|
|
||||||
int (*prepare_transfer_hardware)(struct spi_master *master);
|
int (*prepare_transfer_hardware)(struct spi_master *master);
|
||||||
int (*transfer_one_message)(struct spi_master *master,
|
int (*transfer_one_message)(struct spi_master *master,
|
||||||
@@ -428,6 +447,14 @@ struct spi_master {
|
|||||||
|
|
||||||
/* gpio chip select */
|
/* gpio chip select */
|
||||||
int *cs_gpios;
|
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)
|
static inline void *spi_master_get_devdata(struct spi_master *master)
|
||||||
@@ -512,6 +539,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
|||||||
* (optionally) changing the chipselect status, then starting
|
* (optionally) changing the chipselect status, then starting
|
||||||
* the next transfer or completing this @spi_message.
|
* the next transfer or completing this @spi_message.
|
||||||
* @transfer_list: transfers are sequenced through @spi_message.transfers
|
* @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.
|
* SPI transfers always write the same number of bytes as they read.
|
||||||
* Protocol drivers should always provide @rx_buf and/or @tx_buf.
|
* Protocol drivers should always provide @rx_buf and/or @tx_buf.
|
||||||
@@ -579,6 +608,8 @@ struct spi_transfer {
|
|||||||
|
|
||||||
dma_addr_t tx_dma;
|
dma_addr_t tx_dma;
|
||||||
dma_addr_t rx_dma;
|
dma_addr_t rx_dma;
|
||||||
|
struct sg_table tx_sg;
|
||||||
|
struct sg_table rx_sg;
|
||||||
|
|
||||||
unsigned cs_change:1;
|
unsigned cs_change:1;
|
||||||
unsigned tx_nbits:3;
|
unsigned tx_nbits:3;
|
||||||
|
Reference in New Issue
Block a user