summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-topcliff-pch.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 18:32:00 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 18:32:00 +0100
commit5f0e685f316a1de6d3af8b23eaf46651faca32ab (patch)
treeaf1ed231b7fcfc65b146be59a0aee699aa9f6353 /drivers/spi/spi-topcliff-pch.c
parentMerge tag 'dt-for-linus' of git://git.secretlab.ca/git/linux-2.6 (diff)
parentspi/fsl-espi: Make sure pm is within 2..32 (diff)
downloadlinux-5f0e685f316a1de6d3af8b23eaf46651faca32ab.tar.xz
linux-5f0e685f316a1de6d3af8b23eaf46651faca32ab.zip
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
Pull SPI changes for v3.4 from Grant Likely: "Mostly a bunch of new drivers and driver bug fixes; but this also includes a few patches that create a core message queue infrastructure for the spi subsystem instead of making each driver open code it." * tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits) spi/fsl-espi: Make sure pm is within 2..32 spi/fsl-espi: make the clock computation easier to read spi: sh-hspi: modify write/read method spi: sh-hspi: control spi clock more correctly spi: sh-hspi: convert to using core message queue spi: s3c64xx: Fix build spi: s3c64xx: remove unnecessary callback msg->complete spi: remove redundant variable assignment spi: release lock on error path in spi_pump_messages() spi: Compatibility with direction which is used in samsung DMA operation spi-topcliff-pch: add recovery processing in case wait-event timeout spi-topcliff-pch: supports a spi mode setup and bit order setup by IO control spi-topcliff-pch: Fix issue for transmitting over 4KByte spi-topcliff-pch: Modify pci-bus number dynamically to get DMA device info spi/imx: simplify error handling to free gpios spi: Convert to DEFINE_PCI_DEVICE_TABLE spi: add Broadcom BCM63xx SPI controller driver SPI: add CSR SiRFprimaII SPI controller driver spi-topcliff-pch: fix -Wuninitialized warning spi: Mark spi_register_board_info() __devinit ...
Diffstat (limited to 'drivers/spi/spi-topcliff-pch.c')
-rw-r--r--drivers/spi/spi-topcliff-pch.c113
1 files changed, 84 insertions, 29 deletions
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index 10182eb50068..5c6fa5ed3366 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -196,6 +196,7 @@ struct pch_spi_data {
struct pch_spi_dma_ctrl dma;
int use_dma;
u8 irq_reg_sts;
+ int save_total_len;
};
/**
@@ -216,7 +217,7 @@ struct pch_pd_dev_save {
struct pch_spi_board_data *board_dat;
};
-static struct pci_device_id pch_spi_pcidev_id[] = {
+static DEFINE_PCI_DEVICE_TABLE(pch_spi_pcidev_id) = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, },
@@ -318,22 +319,23 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
data->tx_index = tx_index;
data->rx_index = rx_index;
- }
-
- /* if transfer complete interrupt */
- if (reg_spsr_val & SPSR_FI_BIT) {
- if ((tx_index == bpw_len) && (rx_index == tx_index)) {
- /* disable interrupts */
- pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
-
- /* transfer is completed;
- inform pch_spi_process_messages */
- data->transfer_complete = true;
- data->transfer_active = false;
- wake_up(&data->wait);
- } else {
- dev_err(&data->master->dev,
- "%s : Transfer is not completed", __func__);
+ /* if transfer complete interrupt */
+ if (reg_spsr_val & SPSR_FI_BIT) {
+ if ((tx_index == bpw_len) && (rx_index == tx_index)) {
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+ PCH_ALL);
+
+ /* transfer is completed;
+ inform pch_spi_process_messages */
+ data->transfer_complete = true;
+ data->transfer_active = false;
+ wake_up(&data->wait);
+ } else {
+ dev_err(&data->master->dev,
+ "%s : Transfer is not completed",
+ __func__);
+ }
}
}
}
@@ -822,11 +824,13 @@ static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
rx_dma_buf = data->dma.rx_buf_virt;
for (j = 0; j < data->bpw_len; j++)
*rx_buf++ = *rx_dma_buf++ & 0xFF;
+ data->cur_trans->rx_buf = rx_buf;
} else {
rx_sbuf = data->cur_trans->rx_buf;
rx_dma_sbuf = data->dma.rx_buf_virt;
for (j = 0; j < data->bpw_len; j++)
*rx_sbuf++ = *rx_dma_sbuf++;
+ data->cur_trans->rx_buf = rx_sbuf;
}
}
@@ -852,6 +856,9 @@ static int pch_spi_start_transfer(struct pch_spi_data *data)
rtn = wait_event_interruptible_timeout(data->wait,
data->transfer_complete,
msecs_to_jiffies(2 * HZ));
+ if (!rtn)
+ dev_err(&data->master->dev,
+ "%s wait-event timeout\n", __func__);
dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
DMA_FROM_DEVICE);
@@ -923,7 +930,8 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
dma_cap_set(DMA_SLAVE, mask);
/* Get DMA's dev information */
- dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+ dma_dev = pci_get_bus_and_slot(data->board_dat->pdev->bus->number,
+ PCI_DEVFN(12, 0));
/* Set Tx DMA */
param = &dma->param_tx;
@@ -987,6 +995,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
int i;
int size;
int rem;
+ int head;
unsigned long flags;
struct pch_spi_dma_ctrl *dma;
@@ -1015,6 +1024,11 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
}
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) {
@@ -1029,10 +1043,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
*tx_dma_sbuf++ = *tx_sbuf++;
}
}
+
+ /* Calculate Rx parameter for DMA transmitting */
if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
- num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+ 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;
- rem = data->bpw_len % PCH_DMA_TRANS_SIZE;
} else {
num = 1;
size = data->bpw_len;
@@ -1092,15 +1113,23 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
dma->nent = num;
dma->desc_rx = desc_rx;
- /* TX */
- if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
- num = data->bpw_len / PCH_DMA_TRANS_SIZE;
+ /* 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;
- rem = 16;
} else {
num = 1;
size = data->bpw_len;
rem = data->bpw_len;
+ head = 0;
}
dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
@@ -1110,11 +1139,17 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
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 = rem + size * (i - 1);
+ 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);
@@ -1202,6 +1237,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
data->current_msg->spi->bits_per_word);
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
do {
+ int cnt;
/* If we are already processing a message get the next
transfer structure from the message otherwise retrieve
the 1st transfer request from the message. */
@@ -1221,11 +1257,28 @@ static void pch_spi_process_messages(struct work_struct *pwork)
}
spin_unlock(&data->lock);
+ if (!data->cur_trans->len)
+ goto out;
+ cnt = (data->cur_trans->len - 1) / PCH_BUF_SIZE + 1;
+ data->save_total_len = data->cur_trans->len;
if (data->use_dma) {
- pch_spi_handle_dma(data, &bpw);
- if (!pch_spi_start_transfer(data))
- goto out;
- pch_spi_copy_rx_data_for_dma(data, bpw);
+ int i;
+ char *save_rx_buf = data->cur_trans->rx_buf;
+ for (i = 0; i < cnt; i ++) {
+ pch_spi_handle_dma(data, &bpw);
+ if (!pch_spi_start_transfer(data)) {
+ data->transfer_complete = true;
+ data->current_msg->status = -EIO;
+ data->current_msg->complete
+ (data->current_msg->context);
+ data->bcurrent_msg_processing = false;
+ data->current_msg = NULL;
+ data->cur_trans = NULL;
+ goto out;
+ }
+ pch_spi_copy_rx_data_for_dma(data, bpw);
+ }
+ data->cur_trans->rx_buf = save_rx_buf;
} else {
pch_spi_set_tx(data, &bpw);
pch_spi_set_ir(data);
@@ -1236,6 +1289,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
data->pkt_tx_buff = NULL;
}
/* increment message count */
+ data->cur_trans->len = data->save_total_len;
data->current_msg->actual_length += data->cur_trans->len;
dev_dbg(&data->master->dev,
@@ -1388,6 +1442,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
master->num_chipselect = PCH_MAX_CS;
master->setup = pch_spi_setup;
master->transfer = pch_spi_transfer;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
data->board_dat = board_dat;
data->plat_dev = plat_dev;