in spi-topcliff-pch.c [917:1123]
static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
{
const u8 *tx_buf;
const u16 *tx_sbuf;
u8 *tx_dma_buf;
u16 *tx_dma_sbuf;
struct scatterlist *sg;
struct dma_async_tx_descriptor *desc_tx;
struct dma_async_tx_descriptor *desc_rx;
int num;
int i;
int size;
int rem;
int head;
unsigned long flags;
struct pch_spi_dma_ctrl *dma;
dma = &data->dma;
/* set baud rate if needed */
if (data->cur_trans->speed_hz) {
dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
spin_lock_irqsave(&data->lock, flags);
pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz);
spin_unlock_irqrestore(&data->lock, flags);
}
/* set bits per word if needed */
if (data->cur_trans->bits_per_word &&
(data->current_msg->spi->bits_per_word !=
data->cur_trans->bits_per_word)) {
dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
spin_lock_irqsave(&data->lock, flags);
pch_spi_set_bits_per_word(data->master,
data->cur_trans->bits_per_word);
spin_unlock_irqrestore(&data->lock, flags);
*bpw = data->cur_trans->bits_per_word;
} else {
*bpw = data->current_msg->spi->bits_per_word;
}
data->bpw_len = data->cur_trans->len / (*bpw / 8);
if (data->bpw_len > PCH_BUF_SIZE) {
data->bpw_len = PCH_BUF_SIZE;
data->cur_trans->len -= PCH_BUF_SIZE;
}
/* copy Tx Data */
if (data->cur_trans->tx_buf != NULL) {
if (*bpw == 8) {
tx_buf = data->cur_trans->tx_buf;
tx_dma_buf = dma->tx_buf_virt;
for (i = 0; i < data->bpw_len; i++)
*tx_dma_buf++ = *tx_buf++;
} else {
tx_sbuf = data->cur_trans->tx_buf;
tx_dma_sbuf = dma->tx_buf_virt;
for (i = 0; i < data->bpw_len; i++)
*tx_dma_sbuf++ = *tx_sbuf++;
}
}
/* Calculate Rx parameter for DMA transmitting */
if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
if (data->bpw_len % PCH_DMA_TRANS_SIZE) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE;
} else {
num = data->bpw_len / PCH_DMA_TRANS_SIZE;
rem = PCH_DMA_TRANS_SIZE;
}
size = PCH_DMA_TRANS_SIZE;
} else {
num = 1;
size = data->bpw_len;
rem = data->bpw_len;
}
dev_dbg(&data->master->dev, "%s num=%d size=%d rem=%d\n",
__func__, num, size, rem);
spin_lock_irqsave(&data->lock, flags);
/* set receive fifo threshold and transmit fifo threshold */
pch_spi_setclr_reg(data->master, PCH_SPCR,
((size - 1) << SPCR_RFIC_FIELD) |
(PCH_TX_THOLD << SPCR_TFIC_FIELD),
MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
spin_unlock_irqrestore(&data->lock, flags);
/* RX */
dma->sg_rx_p = kmalloc_array(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC);
if (!dma->sg_rx_p)
return;
sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */
/* offset, length setting */
sg = dma->sg_rx_p;
for (i = 0; i < num; i++, sg++) {
if (i == (num - 2)) {
sg->offset = size * i;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
sg->offset);
sg_dma_len(sg) = rem;
} else if (i == (num - 1)) {
sg->offset = size * (i - 1) + rem;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size,
sg->offset);
sg_dma_len(sg) = size;
} else {
sg->offset = size * i;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size,
sg->offset);
sg_dma_len(sg) = size;
}
sg_dma_address(sg) = dma->rx_buf_dma + sg->offset;
}
sg = dma->sg_rx_p;
desc_rx = dmaengine_prep_slave_sg(dma->chan_rx, sg,
num, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx) {
dev_err(&data->master->dev,
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
return;
}
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE);
desc_rx->callback = pch_dma_rx_complete;
desc_rx->callback_param = data;
dma->nent = num;
dma->desc_rx = desc_rx;
/* Calculate Tx parameter for DMA transmitting */
if (data->bpw_len > PCH_MAX_FIFO_DEPTH) {
head = PCH_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE;
if (data->bpw_len % PCH_DMA_TRANS_SIZE > 4) {
num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE - head;
} else {
num = data->bpw_len / PCH_DMA_TRANS_SIZE;
rem = data->bpw_len % PCH_DMA_TRANS_SIZE +
PCH_DMA_TRANS_SIZE - head;
}
size = PCH_DMA_TRANS_SIZE;
} else {
num = 1;
size = data->bpw_len;
rem = data->bpw_len;
head = 0;
}
dma->sg_tx_p = kmalloc_array(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC);
if (!dma->sg_tx_p)
return;
sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */
/* offset, length setting */
sg = dma->sg_tx_p;
for (i = 0; i < num; i++, sg++) {
if (i == 0) {
sg->offset = 0;
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size + head,
sg->offset);
sg_dma_len(sg) = size + head;
} else if (i == (num - 1)) {
sg->offset = head + size * i;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem,
sg->offset);
sg_dma_len(sg) = rem;
} else {
sg->offset = head + size * i;
sg->offset = sg->offset * (*bpw / 8);
sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size,
sg->offset);
sg_dma_len(sg) = size;
}
sg_dma_address(sg) = dma->tx_buf_dma + sg->offset;
}
sg = dma->sg_tx_p;
desc_tx = dmaengine_prep_slave_sg(dma->chan_tx,
sg, num, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
dev_err(&data->master->dev,
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
return;
}
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE);
desc_tx->callback = NULL;
desc_tx->callback_param = data;
dma->nent = num;
dma->desc_tx = desc_tx;
dev_dbg(&data->master->dev, "%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", __func__);
spin_lock_irqsave(&data->lock, flags);
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
desc_rx->tx_submit(desc_rx);
desc_tx->tx_submit(desc_tx);
spin_unlock_irqrestore(&data->lock, flags);
/* reset transfer complete flag */
data->transfer_complete = false;
}