linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH
@ 2011-06-07  5:50 Tomoya MORINAGA
  2011-06-07  5:50 ` [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Tomoya MORINAGA
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Tomoya MORINAGA @ 2011-06-07  5:50 UTC (permalink / raw)
  To: David Brownell, Grant Likely, spi-devel-general, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux,
	Tomoya MORINAGA

***Modify Grant's comments.
   - Delete unrelated whitespace
   - Prevent device driver from accessing platform data
   - Add __devinit and __devexit
   - Save pdev->dev to pd_dev->dev.parent
   - Have own suspend/resume processing in platform_driver.
   - Care returned value in pch_spi_init
   - Change unregister order

Support ML7213 device of OKI SEMICONDUCTOR.
ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
ML7213 is compatible for Intel EG20T PCH.

Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
---
 drivers/spi/Kconfig            |    5 +-
 drivers/spi/spi_topcliff_pch.c |  599 +++++++++++++++++++++-------------------
 2 files changed, 317 insertions(+), 287 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fc14b8d..ed6134b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -380,12 +380,15 @@ config SPI_TI_SSP
 	  module will be called ti-ssp-spi.
 
 config SPI_TOPCLIFF_PCH
-	tristate "Topcliff PCH SPI Controller"
+	tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI controller"
 	depends on PCI
 	help
 	  SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
 	  used in some x86 embedded processors.
 
+	  This driver also supports the ML7213, a companion chip for the
+	  Atom E6xx series and compatible with the Intel EG20T PCH.
+
 config SPI_TXX9
 	tristate "Toshiba TXx9 SPI controller"
 	depends on GENERIC_GPIO && CPU_TX49XX
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 79e48d4..88bd472 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -26,6 +26,7 @@
 #include <linux/spi/spidev.h>
 #include <linux/module.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 
 /* Register offsets */
 #define PCH_SPCR		0x00	/* SPI control register */
@@ -35,6 +36,7 @@
 #define PCH_SPDRR		0x10	/* SPI read data register */
 #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */
 #define PCH_SRST		0x1C	/* SPI reset register */
+#define PCH_SPI_ADDRESS_SIZE	0x20
 
 #define PCH_SPSR_TFD		0x000007C0
 #define PCH_SPSR_RFD		0x0000F800
@@ -75,7 +77,8 @@
 #define SPSR_FI_BIT		(1 << 2)
 #define SPBRR_SIZE_BIT		(1 << 10)
 
-#define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
+#define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
+				SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
 
 #define SPCR_RFIC_FIELD		20
 #define SPCR_TFIC_FIELD		16
@@ -88,6 +91,16 @@
 #define PCH_CLOCK_HZ		50000000
 #define PCH_MAX_SPBR		1023
 
+/* Definition for ML7213 by OKI SEMICONDUCTOR */
+#define PCI_VENDOR_ID_ROHM		0x10DB
+#define PCI_DEVICE_ID_ML7213_SPI	0x802c
+
+/*
+ * Set the number of SPI instance max
+ * Intel EG20T PCH :		1ch
+ * OKI SEMICONDUCTOR ML7213 IOH :	2ch
+*/
+#define PCH_SPI_MAX_DEV			2
 
 /**
  * struct pch_spi_data - Holds the SPI channel specific details
@@ -121,6 +134,9 @@
  * @cur_trans:			The current transfer that this SPI driver is
  *				handling
  * @board_dat:			Reference to the SPI device data structure
+ * @plat_dev:			platform_device structure
+ * @ch:				SPI channel number
+ * @irq_reg_sts:		Status of IRQ registration
  */
 struct pch_spi_data {
 	void __iomem *io_remap_addr;
@@ -144,27 +160,33 @@ struct pch_spi_data {
 	struct spi_message *current_msg;
 	struct spi_transfer *cur_trans;
 	struct pch_spi_board_data *board_dat;
+	struct platform_device	*plat_dev;
+	int ch;
+	u8 irq_reg_sts;
 };
 
 /**
  * struct pch_spi_board_data - Holds the SPI device specific details
  * @pdev:		Pointer to the PCI device
- * @irq_reg_sts:	Status of IRQ registration
- * @pci_req_sts:	Status of pci_request_regions
  * @suspend_sts:	Status of suspend
- * @data:		Pointer to SPI channel data structure
+ * @num:		The number of SPI device instance
  */
 struct pch_spi_board_data {
 	struct pci_dev *pdev;
-	u8 irq_reg_sts;
-	u8 pci_req_sts;
 	u8 suspend_sts;
-	struct pch_spi_data *data;
+	int num;
+};
+
+struct pch_pd_dev_save {
+	int num;
+	struct platform_device *pd_save[PCH_SPI_MAX_DEV];
+	struct pch_spi_board_data *board_dat;
 };
 
 static struct pci_device_id pch_spi_pcidev_id[] = {
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)},
-	{0,}
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI),    1, },
+	{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, },
+	{ }
 };
 
 /**
@@ -283,11 +305,11 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 static irqreturn_t pch_spi_handler(int irq, void *dev_id)
 {
 	u32 reg_spsr_val;
-	struct pch_spi_data *data;
 	void __iomem *spsr;
 	void __iomem *io_remap_addr;
 	irqreturn_t ret = IRQ_NONE;
-	struct pch_spi_board_data *board_dat = dev_id;
+	struct pch_spi_data *data = dev_id;
+	struct pch_spi_board_data *board_dat = data->board_dat;
 
 	if (board_dat->suspend_sts) {
 		dev_dbg(&board_dat->pdev->dev,
@@ -295,10 +317,8 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
 		return IRQ_NONE;
 	}
 
-	data = board_dat->data;
 	io_remap_addr = data->io_remap_addr;
 	spsr = io_remap_addr + PCH_SPSR;
-
 	reg_spsr_val = ioread32(spsr);
 
 	/* Check if the interrupt is for SPI device */
@@ -412,7 +432,6 @@ static int pch_spi_setup(struct spi_device *pspi)
 
 static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 {
-
 	struct spi_transfer *transfer;
 	struct pch_spi_data *data = spi_master_get_devdata(pspi->master);
 	int retval;
@@ -547,7 +566,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 
 	/* 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)) {
+	   (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__);
 		pch_spi_set_bits_per_word(data->master,
 					  data->cur_trans->bits_per_word);
@@ -621,7 +641,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	data->transfer_active = true;
 }
 
-
 static void pch_spi_nomore_transfer(struct pch_spi_data *data,
 						struct spi_message *pmsg)
 {
@@ -697,7 +716,8 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
 		"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
 
 	/* SPI set enable */
-	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0);
+	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
+			   0);
 
 	/* Wait until the transfer completes; go to sleep after
 				 initiating the transfer. */
@@ -742,7 +762,6 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
 	}
 }
 
-
 static void pch_spi_process_messages(struct work_struct *pwork)
 {
 	struct spi_message *pmsg;
@@ -868,117 +887,49 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	} while (data->cur_trans != NULL);
 }
 
-static void pch_spi_free_resources(struct pch_spi_board_data *board_dat)
+static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
+				   struct pch_spi_data *data)
 {
 	dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
 
 	/* free workqueue */
-	if (board_dat->data->wk != NULL) {
-		destroy_workqueue(board_dat->data->wk);
-		board_dat->data->wk = NULL;
+	if (data->wk != NULL) {
+		destroy_workqueue(data->wk);
+		data->wk = NULL;
 		dev_dbg(&board_dat->pdev->dev,
 			"%s destroy_workqueue invoked successfully\n",
 			__func__);
 	}
-
-	/* disable interrupts & free IRQ */
-	if (board_dat->irq_reg_sts) {
-		/* disable interrupts */
-		pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
-				   PCH_ALL);
-
-		/* free IRQ */
-		free_irq(board_dat->pdev->irq, board_dat);
-
-		dev_dbg(&board_dat->pdev->dev,
-			"%s free_irq invoked successfully\n", __func__);
-
-		board_dat->irq_reg_sts = false;
-	}
-
-	/* unmap PCI base address */
-	if (board_dat->data->io_remap_addr != 0) {
-		pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr);
-
-		board_dat->data->io_remap_addr = 0;
-
-		dev_dbg(&board_dat->pdev->dev,
-			"%s pci_iounmap invoked successfully\n", __func__);
-	}
-
-	/* release PCI region */
-	if (board_dat->pci_req_sts) {
-		pci_release_regions(board_dat->pdev);
-		dev_dbg(&board_dat->pdev->dev,
-			"%s pci_release_regions invoked successfully\n",
-			__func__);
-		board_dat->pci_req_sts = false;
-	}
 }
 
-static int pch_spi_get_resources(struct pch_spi_board_data *board_dat)
+static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
+				 struct pch_spi_data *data)
 {
-	void __iomem *io_remap_addr;
-	int retval;
+	int retval = 0;
+
 	dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
 
 	/* create workqueue */
-	board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
-	if (!board_dat->data->wk) {
+	data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+	if (!data->wk) {
 		dev_err(&board_dat->pdev->dev,
 			"%s create_singlet hread_workqueue failed\n", __func__);
 		retval = -EBUSY;
 		goto err_return;
 	}
 
-	dev_dbg(&board_dat->pdev->dev,
-		"%s create_singlethread_workqueue success\n", __func__);
-
-	retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME);
-	if (retval != 0) {
-		dev_err(&board_dat->pdev->dev,
-			"%s request_region failed\n", __func__);
-		goto err_return;
-	}
-
-	board_dat->pci_req_sts = true;
-
-	io_remap_addr = pci_iomap(board_dat->pdev, 1, 0);
-	if (io_remap_addr == 0) {
-		dev_err(&board_dat->pdev->dev,
-			"%s pci_iomap failed\n", __func__);
-		retval = -ENOMEM;
-		goto err_return;
-	}
-
-	/* calculate base address for all channels */
-	board_dat->data->io_remap_addr = io_remap_addr;
-
 	/* reset PCH SPI h/w */
-	pch_spi_reset(board_dat->data->master);
+	pch_spi_reset(data->master);
 	dev_dbg(&board_dat->pdev->dev,
 		"%s pch_spi_reset invoked successfully\n", __func__);
 
-	/* register IRQ */
-	retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
-			     IRQF_SHARED, KBUILD_MODNAME, board_dat);
-	if (retval != 0) {
-		dev_err(&board_dat->pdev->dev,
-			"%s request_irq failed\n", __func__);
-		goto err_return;
-	}
-
-	dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n",
-		__func__, retval);
-
-	board_dat->irq_reg_sts = true;
 	dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__);
 
 err_return:
 	if (retval != 0) {
 		dev_err(&board_dat->pdev->dev,
 			"%s FAIL:invoking pch_spi_free_resources\n", __func__);
-		pch_spi_free_resources(board_dat);
+		pch_spi_free_resources(board_dat, data);
 	}
 
 	dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval);
@@ -986,255 +937,343 @@ err_return:
 	return retval;
 }
 
-static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 {
-
+	int ret;
 	struct spi_master *master;
+	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+	struct pch_spi_data *data;
 
-	struct pch_spi_board_data *board_dat;
-	int retval;
-
-	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
-	/* allocate memory for private data */
-	board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
-	if (board_dat == NULL) {
-		dev_err(&pdev->dev,
-			" %s memory allocation for private data failed\n",
-			__func__);
-		retval = -ENOMEM;
-		goto err_kmalloc;
-	}
-
-	dev_dbg(&pdev->dev,
-		"%s memory allocation for private data success\n", __func__);
-
-	/* enable PCI device */
-	retval = pci_enable_device(pdev);
-	if (retval != 0) {
-		dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
-
-		goto err_pci_en_device;
+	master = spi_alloc_master(&board_dat->pdev->dev,
+				  sizeof(struct pch_spi_data));
+	if (!master) {
+		dev_err(&plat_dev->dev, "spi_alloc_master[%d] failed.\n",
+			plat_dev->id);
+		return -ENOMEM;
 	}
 
-	dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n",
-		__func__, retval);
+	data = spi_master_get_devdata(master);
+	data->master = master;
 
-	board_dat->pdev = pdev;
+	platform_set_drvdata(plat_dev, data);
 
-	/* alllocate memory for SPI master */
-	master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data));
-	if (master == NULL) {
-		retval = -ENOMEM;
-		dev_err(&pdev->dev, "%s Fail.\n", __func__);
-		goto err_spi_alloc_master;
+	/* baseaddress + 0x20(offset) */
+	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
+						   0x20 * plat_dev->id;
+	if (!data->io_remap_addr) {
+		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
+		ret = -ENOMEM;
+		goto err_pci_iomap;
 	}
 
-	dev_dbg(&pdev->dev,
-		"%s spi_alloc_master returned non NULL\n", __func__);
+	dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n",
+		plat_dev->id, data->io_remap_addr);
 
 	/* initialize members of SPI master */
-	master->bus_num = -1;
+	master->bus_num = plat_dev->id;
 	master->num_chipselect = PCH_MAX_CS;
 	master->setup = pch_spi_setup;
 	master->transfer = pch_spi_transfer;
-	dev_dbg(&pdev->dev,
-		"%s transfer member of SPI master initialized\n", __func__);
 
-	board_dat->data = spi_master_get_devdata(master);
+	data->board_dat = board_dat;
+	data->plat_dev = plat_dev;
+	data->n_curnt_chip = 255;
+	data->status = STATUS_RUNNING;
+	data->ch = plat_dev->id;
 
-	board_dat->data->master = master;
-	board_dat->data->n_curnt_chip = 255;
-	board_dat->data->board_dat = board_dat;
-	board_dat->data->status = STATUS_RUNNING;
+	INIT_LIST_HEAD(&data->queue);
+	spin_lock_init(&data->lock);
+	INIT_WORK(&data->work, pch_spi_process_messages);
+	init_waitqueue_head(&data->wait);
 
-	INIT_LIST_HEAD(&board_dat->data->queue);
-	spin_lock_init(&board_dat->data->lock);
-	INIT_WORK(&board_dat->data->work, pch_spi_process_messages);
-	init_waitqueue_head(&board_dat->data->wait);
-
-	/* allocate resources for PCH SPI */
-	retval = pch_spi_get_resources(board_dat);
-	if (retval) {
-		dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval);
+	ret = pch_spi_get_resources(board_dat, data);
+	if (ret) {
+		dev_err(&plat_dev->dev, "%s fail(retval=%d)\n", __func__, ret);
 		goto err_spi_get_resources;
 	}
 
-	dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n",
-		__func__, retval);
-
-	/* save private data in dev */
-	pci_set_drvdata(pdev, board_dat);
-	dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__);
+	ret = request_irq(board_dat->pdev->irq, pch_spi_handler,
+			  IRQF_SHARED, KBUILD_MODNAME, data);
+	if (ret) {
+		dev_err(&plat_dev->dev,
+			"%s request_irq failed\n", __func__);
+		goto err_request_irq;
+	}
+	data->irq_reg_sts = true;
 
-	/* set master mode */
 	pch_spi_set_master_mode(master);
-	dev_dbg(&pdev->dev,
-		"%s invoked pch_spi_set_master_mode\n", __func__);
 
-	/* Register the controller with the SPI core. */
-	retval = spi_register_master(master);
-	if (retval != 0) {
-		dev_err(&pdev->dev,
+	ret = spi_register_master(master);
+	if (ret != 0) {
+		dev_err(&plat_dev->dev,
 			"%s spi_register_master FAILED\n", __func__);
-		goto err_spi_reg_master;
+		goto err_spi_register_master;
 	}
 
-	dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n",
-		__func__, retval);
-
-
 	return 0;
 
-err_spi_reg_master:
-	spi_unregister_master(master);
+err_spi_register_master:
+	free_irq(board_dat->pdev->irq, board_dat);
+err_request_irq:
+	pch_spi_free_resources(board_dat, data);
 err_spi_get_resources:
-err_spi_alloc_master:
+	pci_iounmap(board_dat->pdev, data->io_remap_addr);
+err_pci_iomap:
 	spi_master_put(master);
-	pci_disable_device(pdev);
-err_pci_en_device:
-	kfree(board_dat);
-err_kmalloc:
-	return retval;
+
+	return ret;
 }
 
-static void pch_spi_remove(struct pci_dev *pdev)
+static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
 {
-	struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
+	struct pch_spi_data *data = platform_get_drvdata(plat_dev);
 	int count;
 
-	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
-
-	if (!board_dat) {
-		dev_err(&pdev->dev,
-			"%s pci_get_drvdata returned NULL\n", __func__);
-		return;
-	}
-
+	dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
+		__func__, plat_dev->id, board_dat->pdev->irq);
 	/* check for any pending messages; no action is taken if the queue
 	 * is still full; but at least we tried.  Unload anyway */
 	count = 500;
-	spin_lock(&board_dat->data->lock);
-	board_dat->data->status = STATUS_EXITING;
-	while ((list_empty(&board_dat->data->queue) == 0) && --count) {
+	spin_lock(&data->lock);
+	data->status = STATUS_EXITING;
+	while ((list_empty(&data->queue) == 0) && --count) {
 		dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
 			__func__);
-		spin_unlock(&board_dat->data->lock);
+		spin_unlock(&data->lock);
 		msleep(PCH_SLEEP_TIME);
-		spin_lock(&board_dat->data->lock);
+		spin_lock(&data->lock);
 	}
-	spin_unlock(&board_dat->data->lock);
-
-	/* Free resources allocated for PCH SPI */
-	pch_spi_free_resources(board_dat);
-
-	spi_unregister_master(board_dat->data->master);
-
-	/* free memory for private data */
-	kfree(board_dat);
+	spin_unlock(&data->lock);
 
-	pci_set_drvdata(pdev, NULL);
+	pch_spi_free_resources(board_dat, data);
+	/* disable interrupts & free IRQ */
+	if (data->irq_reg_sts) {
+		/* disable interrupts */
+		pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+		data->irq_reg_sts = false;
+		free_irq(board_dat->pdev->irq, data);
+	}
 
-	/* disable PCI device */
-	pci_disable_device(pdev);
+	pci_iounmap(board_dat->pdev, data->io_remap_addr);
+	spi_unregister_master(data->master);
+	spi_master_put(data->master);
+	platform_set_drvdata(plat_dev, NULL);
 
-	dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__);
+	return 0;
 }
-
 #ifdef CONFIG_PM
-static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+static int pch_spi_pd_suspend(struct platform_device *pd_dev,
+			      pm_message_t state)
 {
 	u8 count;
-	int retval;
-
-	struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+	struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+	struct pch_spi_data *data = platform_get_drvdata(pd_dev);
 
-	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+	dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__);
 
 	if (!board_dat) {
-		dev_err(&pdev->dev,
+		dev_err(&pd_dev->dev,
 			"%s pci_get_drvdata returned NULL\n", __func__);
 		return -EFAULT;
 	}
 
-	retval = 0;
-	board_dat->suspend_sts = true;
-
 	/* check if the current message is processed:
 	   Only after thats done the transfer will be suspended */
 	count = 255;
-	while ((--count) > 0) {
-		if (!(board_dat->data->bcurrent_msg_processing)) {
-			dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_"
-				"msg_processing = false\n", __func__);
+	while ((--count) > 0)
+		if (!(data->bcurrent_msg_processing)) {
 			break;
-		} else {
-			dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_"
-				"processing = true\n", __func__);
-		}
 		msleep(PCH_SLEEP_TIME);
 	}
 
 	/* Free IRQ */
-	if (board_dat->irq_reg_sts) {
+	if (data->irq_reg_sts) {
 		/* disable all interrupts */
-		pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
-				   PCH_ALL);
-		pch_spi_reset(board_dat->data->master);
-
-		free_irq(board_dat->pdev->irq, board_dat);
+		pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+		pch_spi_reset(data->master);
+		free_irq(board_dat->pdev->irq, data);
 
-		board_dat->irq_reg_sts = false;
-		dev_dbg(&pdev->dev,
+		data->irq_reg_sts = false;
+		dev_dbg(&pd_dev->dev,
 			"%s free_irq invoked successfully.\n", __func__);
 	}
 
+	return 0;
+}
+
+static int pch_spi_pd_resume(struct platform_device *pd_dev)
+{
+	struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
+	struct pch_spi_data *data = platform_get_drvdata(pd_dev);
+	int retval;
+
+	if (!board_dat) {
+		dev_err(&pd_dev->dev,
+			"%s pci_get_drvdata returned NULL\n", __func__);
+		return -EFAULT;
+	}
+
+	if (!data->irq_reg_sts) {
+		/* register IRQ */
+		retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
+				     IRQF_SHARED, KBUILD_MODNAME, data);
+		if (retval < 0) {
+			dev_err(&pd_dev->dev,
+				"%s request_irq failed\n", __func__);
+			return retval;
+		}
+
+		/* reset PCH SPI h/w */
+		pch_spi_reset(data->master);
+		pch_spi_set_master_mode(data->master);
+		data->irq_reg_sts = true;
+	}
+	return 0;
+}
+#else
+#define pch_spi_pd_suspend NULL
+#define pch_spi_pd_resume NULL
+#endif
+
+static struct platform_driver pch_spi_pd_driver = {
+	.driver = {
+		.name = "pch-spi",
+		.owner = THIS_MODULE,
+	},
+	.probe = pch_spi_pd_probe,
+	.remove = __devexit_p(pch_spi_pd_remove),
+	.suspend = pch_spi_pd_suspend,
+	.resume = pch_spi_pd_resume
+};
+
+static int __devinit pch_spi_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct pch_spi_board_data *board_dat;
+	struct platform_device *pd_dev = NULL;
+	int retval;
+	int i;
+	struct pch_pd_dev_save *pd_dev_save;
+
+	pd_dev_save = kzalloc(sizeof(struct pch_pd_dev_save), GFP_KERNEL);
+	if (!pd_dev_save) {
+		dev_err(&pdev->dev, "%s Can't allocate pd_dev_sav\n", __func__);
+		return -ENOMEM;
+	}
+
+	board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
+	if (!board_dat) {
+		dev_err(&pdev->dev, "%s Can't allocate board_dat\n", __func__);
+		retval = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	retval = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (retval) {
+		dev_err(&pdev->dev, "%s request_region failed\n", __func__);
+		goto pci_request_regions;
+	}
+
+	board_dat->pdev = pdev;
+	board_dat->num = id->driver_data;
+	pd_dev_save->num = id->driver_data;
+	pd_dev_save->board_dat = board_dat;
+
+	retval = pci_enable_device(pdev);
+	if (retval) {
+		dev_err(&pdev->dev, "%s pci_enable_device failed\n", __func__);
+		goto pci_enable_device;
+	}
+
+	for (i = 0; i < board_dat->num; i++) {
+		pd_dev = platform_device_alloc("pch-spi", i);
+		if (!pd_dev) {
+			dev_err(&pdev->dev, "platform_device_alloc failed\n");
+			goto err_platform_device;
+		}
+		pd_dev_save->pd_save[i] = pd_dev;
+		pd_dev->dev.parent = &pdev->dev;
+
+		retval = platform_device_add_data(pd_dev, board_dat,
+						  sizeof(*board_dat));
+		if (retval) {
+			dev_err(&pdev->dev,
+				"platform_device_add_data failed\n");
+			platform_device_put(pd_dev);
+			goto err_platform_device;
+		}
+
+		retval = platform_device_add(pd_dev);
+		if (retval) {
+			dev_err(&pdev->dev, "platform_device_add failed\n");
+			platform_device_put(pd_dev);
+			goto err_platform_device;
+		}
+	}
+
+	pci_set_drvdata(pdev, pd_dev_save);
+
+	return 0;
+
+err_platform_device:
+	pci_disable_device(pdev);
+pci_enable_device:
+	pci_release_regions(pdev);
+pci_request_regions:
+	kfree(board_dat);
+err_no_mem:
+	kfree(pd_dev_save);
+
+	return retval;
+}
+
+static void __devexit pch_spi_remove(struct pci_dev *pdev)
+{
+	int i;
+	struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s ENTRY:pdev=%p\n", __func__, pdev);
+
+	for (i = 0; i < pd_dev_save->num; i++)
+		platform_device_unregister(pd_dev_save->pd_save[i]);
+
+	pci_disable_device(pdev);
+	pci_release_regions(pdev);
+	kfree(pd_dev_save->board_dat);
+	kfree(pd_dev_save);
+}
+
+#ifdef CONFIG_PM
+static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	int retval;
+	struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+	pd_dev_save->board_dat->suspend_sts = true;
+
 	/* save config space */
 	retval = pci_save_state(pdev);
-
 	if (retval == 0) {
-		dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n",
-			__func__, retval);
-		/* disable PM notifications */
 		pci_enable_wake(pdev, PCI_D3hot, 0);
-		dev_dbg(&pdev->dev,
-			"%s pci_enable_wake invoked successfully\n", __func__);
-		/* disable PCI device */
 		pci_disable_device(pdev);
-		dev_dbg(&pdev->dev,
-			"%s pci_disable_device invoked successfully\n",
-			__func__);
-		/* move device to D3hot  state */
 		pci_set_power_state(pdev, PCI_D3hot);
-		dev_dbg(&pdev->dev,
-			"%s pci_set_power_state invoked successfully\n",
-			__func__);
 	} else {
 		dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__);
 	}
 
-	dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval);
-
 	return retval;
 }
 
 static int pch_spi_resume(struct pci_dev *pdev)
 {
 	int retval;
-
-	struct pch_spi_board_data *board = pci_get_drvdata(pdev);
+	struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
 	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
 
-	if (!board) {
-		dev_err(&pdev->dev,
-			"%s pci_get_drvdata returned NULL\n", __func__);
-		return -EFAULT;
-	}
-
-	/* move device to DO power state */
 	pci_set_power_state(pdev, PCI_D0);
-
-	/* restore state */
 	pci_restore_state(pdev);
 
 	retval = pci_enable_device(pdev);
@@ -1242,34 +1281,12 @@ static int pch_spi_resume(struct pci_dev *pdev)
 		dev_err(&pdev->dev,
 			"%s pci_enable_device failed\n", __func__);
 	} else {
-		/* disable PM notifications */
 		pci_enable_wake(pdev, PCI_D3hot, 0);
 
-		/* register IRQ handler */
-		if (!board->irq_reg_sts) {
-			/* register IRQ */
-			retval = request_irq(board->pdev->irq, pch_spi_handler,
-					     IRQF_SHARED, KBUILD_MODNAME,
-					     board);
-			if (retval < 0) {
-				dev_err(&pdev->dev,
-					"%s request_irq failed\n", __func__);
-				return retval;
-			}
-			board->irq_reg_sts = true;
-
-			/* reset PCH SPI h/w */
-			pch_spi_reset(board->data->master);
-			pch_spi_set_master_mode(board->data->master);
-
-			/* set suspend status to false */
-			board->suspend_sts = false;
-
-		}
+		/* set suspend status to false */
+		pd_dev_save->board_dat->suspend_sts = false;
 	}
 
-	dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval);
-
 	return retval;
 }
 #else
@@ -1289,15 +1306,25 @@ static struct pci_driver pch_spi_pcidev = {
 
 static int __init pch_spi_init(void)
 {
-	return pci_register_driver(&pch_spi_pcidev);
+	int ret;
+	ret = platform_driver_register(&pch_spi_pd_driver);
+	if (ret)
+		return ret;
+
+	ret = pci_register_driver(&pch_spi_pcidev);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 module_init(pch_spi_init);
 
 static void __exit pch_spi_exit(void)
 {
 	pci_unregister_driver(&pch_spi_pcidev);
+	platform_driver_unregister(&pch_spi_pd_driver);
 }
 module_exit(pch_spi_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver");
+MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
-- 
1.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support
  2011-06-07  5:50 [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
@ 2011-06-07  5:50 ` Tomoya MORINAGA
  2011-06-08 22:52   ` Grant Likely
  2011-06-08  8:35 ` [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
  2011-06-08 22:33 ` Grant Likely
  2 siblings, 1 reply; 9+ messages in thread
From: Tomoya MORINAGA @ 2011-06-07  5:50 UTC (permalink / raw)
  To: David Brownell, Grant Likely, spi-devel-general, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux,
	Tomoya MORINAGA

This patch enables this SPI driver works with DMA mode.

Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
---
 drivers/spi/spi_topcliff_pch.c |  612 ++++++++++++++++++++++++++++++++--------
 1 files changed, 492 insertions(+), 120 deletions(-)

diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 88bd472..f543ff6 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -28,6 +28,9 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
 /* Register offsets */
 #define PCH_SPCR		0x00	/* SPI control register */
 #define PCH_SPBRR		0x04	/* SPI baud rate register */
@@ -36,7 +39,7 @@
 #define PCH_SPDRR		0x10	/* SPI read data register */
 #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */
 #define PCH_SRST		0x1C	/* SPI reset register */
-#define PCH_SPI_ADDRESS_SIZE	0x20
+#define PCH_ADDRESS_SIZE	0x20
 
 #define PCH_SPSR_TFD		0x000007C0
 #define PCH_SPSR_RFD		0x0000F800
@@ -54,8 +57,6 @@
 #define STATUS_EXITING		2
 #define PCH_SLEEP_TIME		10
 
-#define PCH_ADDRESS_SIZE	0x20
-
 #define SSN_LOW			0x02U
 #define SSN_NO_CONTROL		0x00U
 #define PCH_MAX_CS		0xFF
@@ -75,6 +76,7 @@
 #define SPSR_TFI_BIT		(1 << 0)
 #define SPSR_RFI_BIT		(1 << 1)
 #define SPSR_FI_BIT		(1 << 2)
+#define SPSR_ORF_BIT		(1 << 3)
 #define SPBRR_SIZE_BIT		(1 << 10)
 
 #define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
@@ -83,10 +85,9 @@
 #define SPCR_RFIC_FIELD		20
 #define SPCR_TFIC_FIELD		16
 
-#define SPSR_INT_BITS		0x1F
-#define MASK_SPBRR_SPBR_BITS	(~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS	(~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS	(~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS	((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS	(0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS	(0xf << SPCR_TFIC_FIELD)
 
 #define PCH_CLOCK_HZ		50000000
 #define PCH_MAX_SPBR		1023
@@ -102,6 +103,28 @@
 */
 #define PCH_SPI_MAX_DEV			2
 
+#define PCH_BUF_SIZE		4096
+#define PCH_DMA_TRANS_SIZE	12
+
+static int use_dma = 1;
+
+struct pch_spi_dma_ctrl {
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+	struct dma_chan		*chan_tx;
+	struct dma_chan		*chan_rx;
+	struct scatterlist		*sg_tx_p;
+	struct scatterlist		*sg_rx_p;
+	struct scatterlist		sg_tx;
+	struct scatterlist		sg_rx;
+	int				nent;
+	void				*tx_buf_virt;
+	void				*rx_buf_virt;
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+};
 /**
  * struct pch_spi_data - Holds the SPI channel specific details
  * @io_remap_addr:		The remapped PCI base address
@@ -140,6 +163,7 @@
  */
 struct pch_spi_data {
 	void __iomem *io_remap_addr;
+	unsigned long io_base_addr;
 	struct spi_master *master;
 	struct work_struct work;
 	struct workqueue_struct *wk;
@@ -162,6 +186,8 @@ struct pch_spi_data {
 	struct pch_spi_board_data *board_dat;
 	struct platform_device	*plat_dev;
 	int ch;
+	struct pch_spi_dma_ctrl dma;
+	int use_dma;
 	u8 irq_reg_sts;
 };
 
@@ -273,10 +299,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 			reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
 
 			/* reset rx threshold */
-			reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+			reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
 			reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
-			iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
-				 (io_remap_addr + PCH_SPCR));
+
+			iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
 		}
 
 		/* update counts */
@@ -287,12 +313,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 
 	/* if transfer complete interrupt */
 	if (reg_spsr_val & SPSR_FI_BIT) {
-		/* disable FI & RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
-				   SPCR_FIE_BIT | SPCR_RFIE_BIT);
+		if (tx_index < bpw_len)
+			dev_err(&data->master->dev,
+				"%s : Transfer is not completed", __func__);
+		/* 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);
 	}
 }
@@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
 			"%s returning due to suspend\n", __func__);
 		return IRQ_NONE;
 	}
+	if (data->use_dma)
+		return IRQ_NONE;
 
 	io_remap_addr = data->io_remap_addr;
 	spsr = io_remap_addr + PCH_SPSR;
 	reg_spsr_val = ioread32(spsr);
 
+	if (reg_spsr_val & SPSR_ORF_BIT)
+		dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
 	/* Check if the interrupt is for SPI device */
 	if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
 		pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
 	if (n_spbr > PCH_MAX_SPBR)
 		n_spbr = PCH_MAX_SPBR;
 
-	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
 }
 
 /**
@@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s Transfer List not empty. "
 		"Transfer Speed is set.\n", __func__);
 
+	spin_lock_irqsave(&data->lock, flags);
 	/* validate Tx/Rx buffers and Transfer length */
 	list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
 		if (!transfer->tx_buf && !transfer->rx_buf) {
 			dev_err(&pspi->dev,
 				"%s Tx and Rx buffer NULL\n", __func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		if (!transfer->len) {
 			dev_err(&pspi->dev, "%s Transfer length invalid\n",
 				__func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
 			" valid\n", __func__);
 
-		/* if baud rate hs been specified validate the same */
+		/* if baud rate has been specified validate the same */
 		if (transfer->speed_hz > PCH_MAX_BAUDRATE)
 			transfer->speed_hz = PCH_MAX_BAUDRATE;
 
@@ -484,25 +519,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 				retval = -EINVAL;
 				dev_err(&pspi->dev,
 					"%s Invalid bits per word\n", __func__);
-				goto err_out;
+				goto err_return_spinlock;
 			}
 		}
 	}
-
-	spin_lock_irqsave(&data->lock, flags);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	/* We won't process any messages if we have been asked to terminate */
 	if (data->status == STATUS_EXITING) {
 		dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
 		retval = -ESHUTDOWN;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* If suspended ,return -EINVAL */
 	if (data->board_dat->suspend_sts) {
 		dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
 		retval = -EINVAL;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* set status of message */
@@ -510,9 +544,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
 
 	pmsg->status = -EINPROGRESS;
-
+	spin_lock_irqsave(&data->lock, flags);
 	/* add message to queue */
 	list_add_tail(&pmsg->queue, &data->queue);
+	spin_unlock_irqrestore(&data->lock, flags);
+
 	dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
 
 	/* schedule work queue to run */
@@ -521,11 +557,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 
 	retval = 0;
 
-err_return_spinlock:
-	spin_unlock_irqrestore(&data->lock, flags);
 err_out:
 	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
 	return retval;
+err_return_spinlock:
+	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return retval;
 }
 
 static inline void pch_spi_select_chip(struct pch_spi_data *data,
@@ -546,8 +584,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
 	pch_spi_setup_transfer(pspi);
 }
 
-static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
-			   struct spi_message **ppmsg)
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
 {
 	int size;
 	u32 n_writes;
@@ -556,8 +593,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	const u8 *tx_buf;
 	const u16 *tx_sbuf;
 
-	pmsg = *ppmsg;
-
 	/* set baud rate if needed */
 	if (data->cur_trans->speed_hz) {
 		dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
@@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 
 	/* 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)) {
+	    (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__);
 		pch_spi_set_bits_per_word(data->master,
 					  data->cur_trans->bits_per_word);
@@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	data->transfer_active = true;
 }
 
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
-						struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
 {
+	struct spi_message *pmsg;
 	dev_dbg(&data->master->dev, "%s called\n", __func__);
 	/* Invoke complete callback
 	 * [To the spi core..indicating end of transfer] */
@@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
 
 static void pch_spi_set_ir(struct pch_spi_data *data)
 {
-	/* enable interrupts */
-	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+	/* enable interrupts, set threshold, enable SPI */
+	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
 		/* set receive threshold to PCH_RX_THOLD */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD << SPCR_RFIC_FIELD,
-				   ~MASK_RFIC_SPCR_BITS);
-		/* enable FI and RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
-	} else {
+				   PCH_RX_THOLD << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_RFIE_BIT |
+				   SPCR_ORIE_BIT | SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
+	else
 		/* set receive threshold to maximum */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
-				   ~MASK_TFIC_SPCR_BITS);
-		/* enable FI interrupt */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
-	}
-
-	dev_dbg(&data->master->dev,
-		"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
-	/* SPI set enable */
-	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
-			   0);
+				   PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_ORIE_BIT |
+				   SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
 
 	/* Wait until the transfer completes; go to sleep after
 				 initiating the transfer. */
@@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
 	dev_dbg(&data->master->dev,
 		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
 
-	data->transfer_active = false;
-	dev_dbg(&data->master->dev,
-		"%s set data->transfer_active = false\n", __func__);
-
 	/* clear all interrupts */
 	pch_spi_writereg(data->master, PCH_SPSR,
 			 pch_spi_readreg(data->master, PCH_SPSR));
-	/* disable interrupts */
-	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+	/* Disable interrupts and SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
 }
 
 static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
@@ -762,6 +786,328 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
 	}
 }
 
+static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
+{
+	int j;
+	u8 *rx_buf;
+	u16 *rx_sbuf;
+	const u8 *rx_dma_buf;
+	const u16 *rx_dma_sbuf;
+
+	/* copy Rx Data */
+	if (!data->cur_trans->rx_buf)
+		return;
+
+	if (bpw == 8) {
+		rx_buf = data->cur_trans->rx_buf;
+		rx_dma_buf = data->dma.rx_buf_virt;
+		for (j = 0; j < data->bpw_len; j++)
+			*rx_buf++ = *rx_dma_buf++ & 0xFF;
+	} 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++;
+	}
+}
+
+static void pch_spi_start_transfer(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+	unsigned long flags;
+
+	dma = &data->dma;
+
+	spin_lock_irqsave(&data->lock, flags);
+
+	/* disable interrupts, SPI set enable */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* Wait until the transfer completes; go to sleep after
+				 initiating the transfer. */
+	dev_dbg(&data->master->dev,
+		"%s:waiting for transfer to get over\n", __func__);
+	wait_event_interruptible(data->wait, data->transfer_complete);
+
+	dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
+			    DMA_FROM_DEVICE);
+	async_tx_ack(dma->desc_rx);
+	async_tx_ack(dma->desc_tx);
+	kfree(dma->sg_tx_p);
+	kfree(dma->sg_rx_p);
+
+	spin_lock_irqsave(&data->lock, flags);
+	pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+	dev_dbg(&data->master->dev,
+		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+	/* clear fifo threshold, disable interrupts, disable SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
+			   SPCR_SPE_BIT);
+	/* clear all interrupts */
+	pch_spi_writereg(data->master, PCH_SPSR,
+			 pch_spi_readreg(data->master, PCH_SPSR));
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+	struct pch_spi_data *data = arg;
+
+	/* transfer is completed;inform pch_spi_process_messages_dma */
+	data->transfer_complete = true;
+	wake_up_interruptible(&data->wait);
+}
+
+static bool pch_spi_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) &&
+	    (param->dma_dev == chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	struct pch_dma_slave *param;
+	struct pch_spi_dma_ctrl *dma;
+	unsigned int width;
+
+	if (bpw == 8)
+		width = PCH_DMA_WIDTH_1_BYTE;
+	else
+		width = PCH_DMA_WIDTH_2_BYTES;
+
+	dma = &data->dma;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/* Get DMA's dev information */
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+
+	/* Set Tx DMA */
+	param = &dma->param_tx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
+	param->tx_reg = data->io_base_addr + PCH_SPDWR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Tx)\n");
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_tx = chan;
+
+	/* Set Rx DMA */
+	param = &dma->param_rx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
+	param->rx_reg = data->io_base_addr + PCH_SPDRR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Rx)\n");
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_rx = chan;
+}
+
+static void pch_spi_release_dma(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->chan_tx) {
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+	}
+	if (dma->chan_rx) {
+		dma_release_channel(dma->chan_rx);
+		dma->chan_rx = NULL;
+	}
+	return;
+}
+
+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;
+	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);
+
+	/* 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++;
+		}
+	}
+	if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
+		num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+		size = PCH_DMA_TRANS_SIZE;
+		rem = data->bpw_len % 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_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
+			    SPCR_TFIC_FIELD),
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* RX */
+	dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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 == 0) {
+			sg->offset = 0;
+			sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+					num, DMA_FROM_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_rx) {
+		dev_err(&data->master->dev, "%s:device_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;
+
+	/* TX */
+	dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+					sg, num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_tx) {
+		dev_err(&data->master->dev, "%s:device_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, "\n%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;
+}
+
 static void pch_spi_process_messages(struct work_struct *pwork)
 {
 	struct spi_message *pmsg;
@@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
 
 	spin_lock(&data->lock);
-
 	/* check if suspend has been initiated;if yes flush queue */
 	if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
-		dev_dbg(&data->master->dev,
-			"%s suspend/remove initiated,flushing queue\n",
-			__func__);
-
+		dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
+			"flushing queue\n", __func__);
 		list_for_each_entry(pmsg, data->queue.next, queue) {
 			pmsg->status = -EIO;
-
 			if (pmsg->complete != 0) {
 				spin_unlock(&data->lock);
 				pmsg->complete(pmsg->context);
 				spin_lock(&data->lock);
 			}
-
 			/* delete from queue */
 			list_del_init(&pmsg->queue);
 		}
-
 		spin_unlock(&data->lock);
 		return;
 	}
-
 	data->bcurrent_msg_processing = true;
 	dev_dbg(&data->master->dev,
 		"%s Set data->bcurrent_msg_processing= true\n", __func__);
@@ -803,62 +1142,47 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	/* Get the message from the queue and delete it from there. */
 	data->current_msg = list_entry(data->queue.next, struct spi_message,
 					queue);
-
 	list_del_init(&data->current_msg->queue);
-
 	data->current_msg->status = 0;
-
 	pch_spi_select_chip(data, data->current_msg->spi);
-
 	spin_unlock(&data->lock);
 
+	if (data->use_dma)
+		pch_spi_request_dma(data,
+				    data->current_msg->spi->bits_per_word);
 	do {
 		/* If we are already processing a message get the next
 		transfer structure from the message otherwise retrieve
 		the 1st transfer request from the message. */
 		spin_lock(&data->lock);
-
 		if (data->cur_trans == NULL) {
 			data->cur_trans =
-			    list_entry(data->current_msg->transfers.
-				       next, struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting 1st transfer message\n", __func__);
+				list_entry(data->current_msg->transfers.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting 1st transfer message\n", __func__);
 		} else {
 			data->cur_trans =
-			    list_entry(data->cur_trans->transfer_list.next,
-				       struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting next transfer message\n",
-				__func__);
+				list_entry(data->cur_trans->transfer_list.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting next transfer message\n", __func__);
 		}
-
 		spin_unlock(&data->lock);
 
-		pch_spi_set_tx(data, &bpw, &pmsg);
-
-		/* Control interrupt*/
-		pch_spi_set_ir(data);
-
-		/* Disable SPI transfer */
-		pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
-				   SPCR_SPE_BIT);
-
-		/* clear FIFO */
-		pch_spi_clear_fifo(data->master);
-
-		/* copy Rx Data */
-		pch_spi_copy_rx_data(data, bpw);
-
-		/* free memory */
-		kfree(data->pkt_rx_buff);
-		data->pkt_rx_buff = NULL;
-
-		kfree(data->pkt_tx_buff);
-		data->pkt_tx_buff = NULL;
-
+		if (data->use_dma) {
+			pch_spi_handle_dma(data, &bpw);
+			pch_spi_start_transfer(data);
+			pch_spi_copy_rx_data_for_dma(data, bpw);
+		} else {
+			pch_spi_set_tx(data, &bpw);
+			pch_spi_set_ir(data);
+			pch_spi_copy_rx_data(data, bpw);
+			kfree(data->pkt_rx_buff);
+			data->pkt_rx_buff = NULL;
+			kfree(data->pkt_tx_buff);
+			data->pkt_tx_buff = NULL;
+		}
 		/* increment message count */
 		data->current_msg->actual_length += data->cur_trans->len;
 
@@ -875,16 +1199,17 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 		}
 
 		spin_lock(&data->lock);
-
 		/* No more transfer in this message. */
 		if ((data->cur_trans->transfer_list.next) ==
 		    &(data->current_msg->transfers)) {
-			pch_spi_nomore_transfer(data, pmsg);
+			pch_spi_nomore_transfer(data);
 		}
-
 		spin_unlock(&data->lock);
 
 	} while (data->cur_trans != NULL);
+
+	if (data->use_dma)
+		pch_spi_release_dma(data);
 }
 
 static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
@@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
 
 	/* create workqueue */
 	data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+
 	if (!data->wk) {
 		dev_err(&board_dat->pdev->dev,
 			"%s create_singlet hread_workqueue failed\n", __func__);
@@ -937,6 +1263,35 @@ err_return:
 	return retval;
 }
 
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+			     struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->tx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->tx_buf_virt, dma->tx_buf_dma);
+	if (dma->rx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->rx_buf_virt, dma->rx_buf_dma);
+	return;
+}
+
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+			      struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	/* Get Consistent memory for Tx DMA */
+	dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+	/* Get Consistent memory for Rx DMA */
+	dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
+
 static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 {
 	int ret;
@@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data;
 
+	dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
+
 	master = spi_alloc_master(&board_dat->pdev->dev,
 				  sizeof(struct pch_spi_data));
 	if (!master) {
@@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 
 	platform_set_drvdata(plat_dev, data);
 
-	/* baseaddress + 0x20(offset) */
+	/* baseaddress + address offset) */
+	data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
-						   0x20 * plat_dev->id;
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	if (!data->io_remap_addr) {
 		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
 		ret = -ENOMEM;
@@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	data->n_curnt_chip = 255;
 	data->status = STATUS_RUNNING;
 	data->ch = plat_dev->id;
+	data->use_dma = use_dma;
 
 	INIT_LIST_HEAD(&data->queue);
 	spin_lock_init(&data->lock);
@@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 		goto err_spi_register_master;
 	}
 
+	if (use_dma) {
+		dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+		pch_alloc_dma_buf(board_dat, data);
+	}
+
 	return 0;
 
 err_spi_register_master:
@@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data = platform_get_drvdata(plat_dev);
 	int count;
+	unsigned long flags;
 
 	dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
 		__func__, plat_dev->id, board_dat->pdev->irq);
+
+	if (use_dma)
+		pch_free_dma_buf(board_dat, data);
+
 	/* check for any pending messages; no action is taken if the queue
 	 * is still full; but at least we tried.  Unload anyway */
 	count = 500;
-	spin_lock(&data->lock);
+	spin_lock_irqsave(&data->lock, flags);
 	data->status = STATUS_EXITING;
 	while ((list_empty(&data->queue) == 0) && --count) {
 		dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
 			__func__);
-		spin_unlock(&data->lock);
+		spin_unlock_irqrestore(&data->lock, flags);
 		msleep(PCH_SLEEP_TIME);
-		spin_lock(&data->lock);
+		spin_lock_irqsave(&data->lock, flags);
 	}
-	spin_unlock(&data->lock);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	pch_spi_free_resources(board_dat, data);
 	/* disable interrupts & free IRQ */
@@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
 	/* check if the current message is processed:
 	   Only after thats done the transfer will be suspended */
 	count = 255;
-	while ((--count) > 0)
-		if (!(data->bcurrent_msg_processing)) {
+	while ((--count) > 0) {
+		if (!(data->bcurrent_msg_processing))
 			break;
 		msleep(PCH_SLEEP_TIME);
 	}
@@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
 				"%s request_irq failed\n", __func__);
 			return retval;
 		}
-
 		/* reset PCH SPI h/w */
 		pch_spi_reset(data->master);
 		pch_spi_set_master_mode(data->master);
@@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
 	}
 
 	pci_set_drvdata(pdev, pd_dev_save);
-
 	return 0;
 
 err_platform_device:
@@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
 }
 module_exit(pch_spi_exit);
 
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+		 "to use DMA for data transfers pass 1 else 0; default 1");
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
-- 
1.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH
  2011-06-07  5:50 [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
  2011-06-07  5:50 ` [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Tomoya MORINAGA
@ 2011-06-08  8:35 ` Tomoya MORINAGA
  2011-06-08 14:44   ` Grant Likely
  2011-06-08 22:33 ` Grant Likely
  2 siblings, 1 reply; 9+ messages in thread
From: Tomoya MORINAGA @ 2011-06-08  8:35 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: David Brownell, Grant Likely, spi-devel-general, linux-kernel,
	qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux

Hi Grant,

Why don't you give us your comment/opinion at all for our v4 patch ??
We have been long waiting since 27-April.

(2011/06/07 14:50), Tomoya MORINAGA wrote:
> ***Modify Grant's comments.
>     - Delete unrelated whitespace
>     - Prevent device driver from accessing platform data
>     - Add __devinit and __devexit
>     - Save pdev->dev to pd_dev->dev.parent
>     - Have own suspend/resume processing in platform_driver.
>     - Care returned value in pch_spi_init
>     - Change unregister order
> 
> Support ML7213 device of OKI SEMICONDUCTOR.
> ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
> ML7213 is compatible for Intel EG20T PCH.
> 
> Signed-off-by: Tomoya MORINAGA<tomoya-linux@dsn.okisemi.com>
> ---
>   drivers/spi/Kconfig            |    5 +-
>   drivers/spi/spi_topcliff_pch.c |  599 +++++++++++++++++++++-------------------
>   2 files changed, 317 insertions(+), 287 deletions(-)
> 
-- 
tomoya
OKI SEMICONDUCTOR CO., LTD.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH
  2011-06-08  8:35 ` [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
@ 2011-06-08 14:44   ` Grant Likely
  2011-06-09  1:04     ` Tomoya MORINAGA
  0 siblings, 1 reply; 9+ messages in thread
From: Grant Likely @ 2011-06-08 14:44 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: David Brownell, spi-devel-general, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux

On Wed, Jun 08, 2011 at 05:35:19PM +0900, Tomoya MORINAGA wrote:
> Hi Grant,
> 
> Why don't you give us your comment/opinion at all for our v4 patch ??
> We have been long waiting since 27-April.

Sorry about that Tomoya.  I got completely overloaded at the end of
April and all of May.  I ended up dropping a lot of patches on the
floor because of it.  I'm not purposefully ignoring you!

I'll look at them now.

g.

> 
> (2011/06/07 14:50), Tomoya MORINAGA wrote:
> > ***Modify Grant's comments.
> >     - Delete unrelated whitespace
> >     - Prevent device driver from accessing platform data
> >     - Add __devinit and __devexit
> >     - Save pdev->dev to pd_dev->dev.parent
> >     - Have own suspend/resume processing in platform_driver.
> >     - Care returned value in pch_spi_init
> >     - Change unregister order
> > 
> > Support ML7213 device of OKI SEMICONDUCTOR.
> > ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
> > ML7213 is compatible for Intel EG20T PCH.
> > 
> > Signed-off-by: Tomoya MORINAGA<tomoya-linux@dsn.okisemi.com>
> > ---
> >   drivers/spi/Kconfig            |    5 +-
> >   drivers/spi/spi_topcliff_pch.c |  599 +++++++++++++++++++++-------------------
> >   2 files changed, 317 insertions(+), 287 deletions(-)
> > 
> -- 
> tomoya
> OKI SEMICONDUCTOR CO., LTD.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH
  2011-06-07  5:50 [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
  2011-06-07  5:50 ` [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Tomoya MORINAGA
  2011-06-08  8:35 ` [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
@ 2011-06-08 22:33 ` Grant Likely
  2 siblings, 0 replies; 9+ messages in thread
From: Grant Likely @ 2011-06-08 22:33 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: David Brownell, spi-devel-general, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux

On Tue, Jun 07, 2011 at 02:50:10PM +0900, Tomoya MORINAGA wrote:
> ***Modify Grant's comments.
>    - Delete unrelated whitespace
>    - Prevent device driver from accessing platform data
>    - Add __devinit and __devexit
>    - Save pdev->dev to pd_dev->dev.parent
>    - Have own suspend/resume processing in platform_driver.
>    - Care returned value in pch_spi_init
>    - Change unregister order
> 
> Support ML7213 device of OKI SEMICONDUCTOR.
> ML7213 is companion chip of Intel Atom E6xx series for IVI(In-Vehicle Infotainment).
> ML7213 is compatible for Intel EG20T PCH.
> 
> Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
> ---

Hi Tomoya,

comment below...

> -static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  {
> -
> +	int ret;
>  	struct spi_master *master;
> +	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
> +	struct pch_spi_data *data;
>  
> -	struct pch_spi_board_data *board_dat;
> -	int retval;
> -
> -	dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
> -
> -	/* allocate memory for private data */
> -	board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
> -	if (board_dat == NULL) {
> -		dev_err(&pdev->dev,
> -			" %s memory allocation for private data failed\n",
> -			__func__);
> -		retval = -ENOMEM;
> -		goto err_kmalloc;
> -	}
> -
> -	dev_dbg(&pdev->dev,
> -		"%s memory allocation for private data success\n", __func__);
> -
> -	/* enable PCI device */
> -	retval = pci_enable_device(pdev);
> -	if (retval != 0) {
> -		dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
> -
> -		goto err_pci_en_device;
> +	master = spi_alloc_master(&board_dat->pdev->dev,
> +				  sizeof(struct pch_spi_data));
> +	if (!master) {
> +		dev_err(&plat_dev->dev, "spi_alloc_master[%d] failed.\n",
> +			plat_dev->id);
> +		return -ENOMEM;
>  	}
>  
> -	dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n",
> -		__func__, retval);
> +	data = spi_master_get_devdata(master);
> +	data->master = master;
>  
> -	board_dat->pdev = pdev;
> +	platform_set_drvdata(plat_dev, data);
>  
> -	/* alllocate memory for SPI master */
> -	master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data));
> -	if (master == NULL) {
> -		retval = -ENOMEM;
> -		dev_err(&pdev->dev, "%s Fail.\n", __func__);
> -		goto err_spi_alloc_master;
> +	/* baseaddress + 0x20(offset) */
> +	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
> +						   0x20 * plat_dev->id;
> +	if (!data->io_remap_addr) {
> +		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
> +		ret = -ENOMEM;
> +		goto err_pci_iomap;
>  	}
>  
> -	dev_dbg(&pdev->dev,
> -		"%s spi_alloc_master returned non NULL\n", __func__);
> +	dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n",
> +		plat_dev->id, data->io_remap_addr);
>  
>  	/* initialize members of SPI master */
> -	master->bus_num = -1;
> +	master->bus_num = plat_dev->id;

This shouldn't be here.  The bus id should be dynamically allocated,
and using the plat_dev->id assumes that there are no other spi busses
in the system, which is a bad assumption.

I picked up the patch (it's about time I guess, I've left this out
alone for too long), but I've dropped this hunk.

You can post a followup patch if it broke anything.

g.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support
  2011-06-07  5:50 ` [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Tomoya MORINAGA
@ 2011-06-08 22:52   ` Grant Likely
  0 siblings, 0 replies; 9+ messages in thread
From: Grant Likely @ 2011-06-08 22:52 UTC (permalink / raw)
  To: Tomoya MORINAGA
  Cc: David Brownell, spi-devel-general, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux

On Tue, Jun 07, 2011 at 02:50:11PM +0900, Tomoya MORINAGA wrote:
> This patch enables this SPI driver works with DMA mode.
> 
> Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
> ---
>  drivers/spi/spi_topcliff_pch.c |  612 ++++++++++++++++++++++++++++++++--------
>  1 files changed, 492 insertions(+), 120 deletions(-)

Merged thanks.

BTW, please be aware and careful of whitespace changes.  There were
quite a few unrelated hunks in both this patch and the last one that
have no functional change, and just make it harder to go through the
real changes.  Basically, unless you're touching the line immediately
before or immediately after, don't cleanup whitespace in patches with
functional changes.

Also, I've not reviewed this driver very carefully, so I haven't
inspected the DMA code for correctness.

g.

> 
> diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
> index 88bd472..f543ff6 100644
> --- a/drivers/spi/spi_topcliff_pch.c
> +++ b/drivers/spi/spi_topcliff_pch.c
> @@ -28,6 +28,9 @@
>  #include <linux/device.h>
>  #include <linux/platform_device.h>
>  
> +#include <linux/dmaengine.h>
> +#include <linux/pch_dma.h>
> +
>  /* Register offsets */
>  #define PCH_SPCR		0x00	/* SPI control register */
>  #define PCH_SPBRR		0x04	/* SPI baud rate register */
> @@ -36,7 +39,7 @@
>  #define PCH_SPDRR		0x10	/* SPI read data register */
>  #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */
>  #define PCH_SRST		0x1C	/* SPI reset register */
> -#define PCH_SPI_ADDRESS_SIZE	0x20
> +#define PCH_ADDRESS_SIZE	0x20
>  
>  #define PCH_SPSR_TFD		0x000007C0
>  #define PCH_SPSR_RFD		0x0000F800
> @@ -54,8 +57,6 @@
>  #define STATUS_EXITING		2
>  #define PCH_SLEEP_TIME		10
>  
> -#define PCH_ADDRESS_SIZE	0x20
> -
>  #define SSN_LOW			0x02U
>  #define SSN_NO_CONTROL		0x00U
>  #define PCH_MAX_CS		0xFF
> @@ -75,6 +76,7 @@
>  #define SPSR_TFI_BIT		(1 << 0)
>  #define SPSR_RFI_BIT		(1 << 1)
>  #define SPSR_FI_BIT		(1 << 2)
> +#define SPSR_ORF_BIT		(1 << 3)
>  #define SPBRR_SIZE_BIT		(1 << 10)
>  
>  #define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
> @@ -83,10 +85,9 @@
>  #define SPCR_RFIC_FIELD		20
>  #define SPCR_TFIC_FIELD		16
>  
> -#define SPSR_INT_BITS		0x1F
> -#define MASK_SPBRR_SPBR_BITS	(~((1 << 10) - 1))
> -#define MASK_RFIC_SPCR_BITS	(~(0xf << 20))
> -#define MASK_TFIC_SPCR_BITS	(~(0xf000f << 12))
> +#define MASK_SPBRR_SPBR_BITS	((1 << 10) - 1)
> +#define MASK_RFIC_SPCR_BITS	(0xf << SPCR_RFIC_FIELD)
> +#define MASK_TFIC_SPCR_BITS	(0xf << SPCR_TFIC_FIELD)
>  
>  #define PCH_CLOCK_HZ		50000000
>  #define PCH_MAX_SPBR		1023
> @@ -102,6 +103,28 @@
>  */
>  #define PCH_SPI_MAX_DEV			2
>  
> +#define PCH_BUF_SIZE		4096
> +#define PCH_DMA_TRANS_SIZE	12
> +
> +static int use_dma = 1;
> +
> +struct pch_spi_dma_ctrl {
> +	struct dma_async_tx_descriptor	*desc_tx;
> +	struct dma_async_tx_descriptor	*desc_rx;
> +	struct pch_dma_slave		param_tx;
> +	struct pch_dma_slave		param_rx;
> +	struct dma_chan		*chan_tx;
> +	struct dma_chan		*chan_rx;
> +	struct scatterlist		*sg_tx_p;
> +	struct scatterlist		*sg_rx_p;
> +	struct scatterlist		sg_tx;
> +	struct scatterlist		sg_rx;
> +	int				nent;
> +	void				*tx_buf_virt;
> +	void				*rx_buf_virt;
> +	dma_addr_t			tx_buf_dma;
> +	dma_addr_t			rx_buf_dma;
> +};
>  /**
>   * struct pch_spi_data - Holds the SPI channel specific details
>   * @io_remap_addr:		The remapped PCI base address
> @@ -140,6 +163,7 @@
>   */
>  struct pch_spi_data {
>  	void __iomem *io_remap_addr;
> +	unsigned long io_base_addr;
>  	struct spi_master *master;
>  	struct work_struct work;
>  	struct workqueue_struct *wk;
> @@ -162,6 +186,8 @@ struct pch_spi_data {
>  	struct pch_spi_board_data *board_dat;
>  	struct platform_device	*plat_dev;
>  	int ch;
> +	struct pch_spi_dma_ctrl dma;
> +	int use_dma;
>  	u8 irq_reg_sts;
>  };
>  
> @@ -273,10 +299,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
>  			reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
>  
>  			/* reset rx threshold */
> -			reg_spcr_val &= MASK_RFIC_SPCR_BITS;
> +			reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
>  			reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
> -			iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
> -				 (io_remap_addr + PCH_SPCR));
> +
> +			iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
>  		}
>  
>  		/* update counts */
> @@ -287,12 +313,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
>  
>  	/* if transfer complete interrupt */
>  	if (reg_spsr_val & SPSR_FI_BIT) {
> -		/* disable FI & RFI interrupts */
> -		pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
> -				   SPCR_FIE_BIT | SPCR_RFIE_BIT);
> +		if (tx_index < bpw_len)
> +			dev_err(&data->master->dev,
> +				"%s : Transfer is not completed", __func__);
> +		/* 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);
>  	}
>  }
> @@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
>  			"%s returning due to suspend\n", __func__);
>  		return IRQ_NONE;
>  	}
> +	if (data->use_dma)
> +		return IRQ_NONE;
>  
>  	io_remap_addr = data->io_remap_addr;
>  	spsr = io_remap_addr + PCH_SPSR;
>  	reg_spsr_val = ioread32(spsr);
>  
> +	if (reg_spsr_val & SPSR_ORF_BIT)
> +		dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
> +
>  	/* Check if the interrupt is for SPI device */
>  	if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
>  		pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
> @@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
>  	if (n_spbr > PCH_MAX_SPBR)
>  		n_spbr = PCH_MAX_SPBR;
>  
> -	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
> +	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
>  }
>  
>  /**
> @@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
>  	dev_dbg(&pspi->dev, "%s Transfer List not empty. "
>  		"Transfer Speed is set.\n", __func__);
>  
> +	spin_lock_irqsave(&data->lock, flags);
>  	/* validate Tx/Rx buffers and Transfer length */
>  	list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
>  		if (!transfer->tx_buf && !transfer->rx_buf) {
>  			dev_err(&pspi->dev,
>  				"%s Tx and Rx buffer NULL\n", __func__);
>  			retval = -EINVAL;
> -			goto err_out;
> +			goto err_return_spinlock;
>  		}
>  
>  		if (!transfer->len) {
>  			dev_err(&pspi->dev, "%s Transfer length invalid\n",
>  				__func__);
>  			retval = -EINVAL;
> -			goto err_out;
> +			goto err_return_spinlock;
>  		}
>  
>  		dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
>  			" valid\n", __func__);
>  
> -		/* if baud rate hs been specified validate the same */
> +		/* if baud rate has been specified validate the same */
>  		if (transfer->speed_hz > PCH_MAX_BAUDRATE)
>  			transfer->speed_hz = PCH_MAX_BAUDRATE;
>  
> @@ -484,25 +519,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
>  				retval = -EINVAL;
>  				dev_err(&pspi->dev,
>  					"%s Invalid bits per word\n", __func__);
> -				goto err_out;
> +				goto err_return_spinlock;
>  			}
>  		}
>  	}
> -
> -	spin_lock_irqsave(&data->lock, flags);
> +	spin_unlock_irqrestore(&data->lock, flags);
>  
>  	/* We won't process any messages if we have been asked to terminate */
>  	if (data->status == STATUS_EXITING) {
>  		dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
>  		retval = -ESHUTDOWN;
> -		goto err_return_spinlock;
> +		goto err_out;
>  	}
>  
>  	/* If suspended ,return -EINVAL */
>  	if (data->board_dat->suspend_sts) {
>  		dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
>  		retval = -EINVAL;
> -		goto err_return_spinlock;
> +		goto err_out;
>  	}
>  
>  	/* set status of message */
> @@ -510,9 +544,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
>  	dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
>  
>  	pmsg->status = -EINPROGRESS;
> -
> +	spin_lock_irqsave(&data->lock, flags);
>  	/* add message to queue */
>  	list_add_tail(&pmsg->queue, &data->queue);
> +	spin_unlock_irqrestore(&data->lock, flags);
> +
>  	dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
>  
>  	/* schedule work queue to run */
> @@ -521,11 +557,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
>  
>  	retval = 0;
>  
> -err_return_spinlock:
> -	spin_unlock_irqrestore(&data->lock, flags);
>  err_out:
>  	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
>  	return retval;
> +err_return_spinlock:
> +	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
> +	spin_unlock_irqrestore(&data->lock, flags);
> +	return retval;
>  }
>  
>  static inline void pch_spi_select_chip(struct pch_spi_data *data,
> @@ -546,8 +584,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
>  	pch_spi_setup_transfer(pspi);
>  }
>  
> -static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
> -			   struct spi_message **ppmsg)
> +static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
>  {
>  	int size;
>  	u32 n_writes;
> @@ -556,8 +593,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
>  	const u8 *tx_buf;
>  	const u16 *tx_sbuf;
>  
> -	pmsg = *ppmsg;
> -
>  	/* set baud rate if needed */
>  	if (data->cur_trans->speed_hz) {
>  		dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
> @@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
>  
>  	/* 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)) {
> +	    (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__);
>  		pch_spi_set_bits_per_word(data->master,
>  					  data->cur_trans->bits_per_word);
> @@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
>  	data->transfer_active = true;
>  }
>  
> -static void pch_spi_nomore_transfer(struct pch_spi_data *data,
> -						struct spi_message *pmsg)
> +static void pch_spi_nomore_transfer(struct pch_spi_data *data)
>  {
> +	struct spi_message *pmsg;
>  	dev_dbg(&data->master->dev, "%s called\n", __func__);
>  	/* Invoke complete callback
>  	 * [To the spi core..indicating end of transfer] */
> @@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
>  
>  static void pch_spi_set_ir(struct pch_spi_data *data)
>  {
> -	/* enable interrupts */
> -	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
> +	/* enable interrupts, set threshold, enable SPI */
> +	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
>  		/* set receive threshold to PCH_RX_THOLD */
>  		pch_spi_setclr_reg(data->master, PCH_SPCR,
> -				   PCH_RX_THOLD << SPCR_RFIC_FIELD,
> -				   ~MASK_RFIC_SPCR_BITS);
> -		/* enable FI and RFI interrupts */
> -		pch_spi_setclr_reg(data->master, PCH_SPCR,
> -				   SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
> -	} else {
> +				   PCH_RX_THOLD << SPCR_RFIC_FIELD |
> +				   SPCR_FIE_BIT | SPCR_RFIE_BIT |
> +				   SPCR_ORIE_BIT | SPCR_SPE_BIT,
> +				   MASK_RFIC_SPCR_BITS | PCH_ALL);
> +	else
>  		/* set receive threshold to maximum */
>  		pch_spi_setclr_reg(data->master, PCH_SPCR,
> -				   PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
> -				   ~MASK_TFIC_SPCR_BITS);
> -		/* enable FI interrupt */
> -		pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
> -	}
> -
> -	dev_dbg(&data->master->dev,
> -		"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
> -
> -	/* SPI set enable */
> -	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
> -			   0);
> +				   PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
> +				   SPCR_FIE_BIT | SPCR_ORIE_BIT |
> +				   SPCR_SPE_BIT,
> +				   MASK_RFIC_SPCR_BITS | PCH_ALL);
>  
>  	/* Wait until the transfer completes; go to sleep after
>  				 initiating the transfer. */
> @@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
>  	dev_dbg(&data->master->dev,
>  		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
>  
> -	data->transfer_active = false;
> -	dev_dbg(&data->master->dev,
> -		"%s set data->transfer_active = false\n", __func__);
> -
>  	/* clear all interrupts */
>  	pch_spi_writereg(data->master, PCH_SPSR,
>  			 pch_spi_readreg(data->master, PCH_SPSR));
> -	/* disable interrupts */
> -	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
> +	/* Disable interrupts and SPI transfer */
> +	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
> +	/* clear FIFO */
> +	pch_spi_clear_fifo(data->master);
>  }
>  
>  static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
> @@ -762,6 +786,328 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
>  	}
>  }
>  
> +static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
> +{
> +	int j;
> +	u8 *rx_buf;
> +	u16 *rx_sbuf;
> +	const u8 *rx_dma_buf;
> +	const u16 *rx_dma_sbuf;
> +
> +	/* copy Rx Data */
> +	if (!data->cur_trans->rx_buf)
> +		return;
> +
> +	if (bpw == 8) {
> +		rx_buf = data->cur_trans->rx_buf;
> +		rx_dma_buf = data->dma.rx_buf_virt;
> +		for (j = 0; j < data->bpw_len; j++)
> +			*rx_buf++ = *rx_dma_buf++ & 0xFF;
> +	} 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++;
> +	}
> +}
> +
> +static void pch_spi_start_transfer(struct pch_spi_data *data)
> +{
> +	struct pch_spi_dma_ctrl *dma;
> +	unsigned long flags;
> +
> +	dma = &data->dma;
> +
> +	spin_lock_irqsave(&data->lock, flags);
> +
> +	/* disable interrupts, SPI set enable */
> +	pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
> +
> +	spin_unlock_irqrestore(&data->lock, flags);
> +
> +	/* Wait until the transfer completes; go to sleep after
> +				 initiating the transfer. */
> +	dev_dbg(&data->master->dev,
> +		"%s:waiting for transfer to get over\n", __func__);
> +	wait_event_interruptible(data->wait, data->transfer_complete);
> +
> +	dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
> +			    DMA_FROM_DEVICE);
> +	async_tx_ack(dma->desc_rx);
> +	async_tx_ack(dma->desc_tx);
> +	kfree(dma->sg_tx_p);
> +	kfree(dma->sg_rx_p);
> +
> +	spin_lock_irqsave(&data->lock, flags);
> +	pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
> +	dev_dbg(&data->master->dev,
> +		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
> +
> +	/* clear fifo threshold, disable interrupts, disable SPI transfer */
> +	pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
> +			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
> +			   SPCR_SPE_BIT);
> +	/* clear all interrupts */
> +	pch_spi_writereg(data->master, PCH_SPSR,
> +			 pch_spi_readreg(data->master, PCH_SPSR));
> +	/* clear FIFO */
> +	pch_spi_clear_fifo(data->master);
> +
> +	spin_unlock_irqrestore(&data->lock, flags);
> +}
> +
> +static void pch_dma_rx_complete(void *arg)
> +{
> +	struct pch_spi_data *data = arg;
> +
> +	/* transfer is completed;inform pch_spi_process_messages_dma */
> +	data->transfer_complete = true;
> +	wake_up_interruptible(&data->wait);
> +}
> +
> +static bool pch_spi_filter(struct dma_chan *chan, void *slave)
> +{
> +	struct pch_dma_slave *param = slave;
> +
> +	if ((chan->chan_id == param->chan_id) &&
> +	    (param->dma_dev == chan->device->dev)) {
> +		chan->private = param;
> +		return true;
> +	} else {
> +		return false;
> +	}
> +}
> +
> +static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
> +{
> +	dma_cap_mask_t mask;
> +	struct dma_chan *chan;
> +	struct pci_dev *dma_dev;
> +	struct pch_dma_slave *param;
> +	struct pch_spi_dma_ctrl *dma;
> +	unsigned int width;
> +
> +	if (bpw == 8)
> +		width = PCH_DMA_WIDTH_1_BYTE;
> +	else
> +		width = PCH_DMA_WIDTH_2_BYTES;
> +
> +	dma = &data->dma;
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	/* Get DMA's dev information */
> +	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
> +
> +	/* Set Tx DMA */
> +	param = &dma->param_tx;
> +	param->dma_dev = &dma_dev->dev;
> +	param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
> +	param->tx_reg = data->io_base_addr + PCH_SPDWR;
> +	param->width = width;
> +	chan = dma_request_channel(mask, pch_spi_filter, param);
> +	if (!chan) {
> +		dev_err(&data->master->dev,
> +			"ERROR: dma_request_channel FAILS(Tx)\n");
> +		data->use_dma = 0;
> +		return;
> +	}
> +	dma->chan_tx = chan;
> +
> +	/* Set Rx DMA */
> +	param = &dma->param_rx;
> +	param->dma_dev = &dma_dev->dev;
> +	param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
> +	param->rx_reg = data->io_base_addr + PCH_SPDRR;
> +	param->width = width;
> +	chan = dma_request_channel(mask, pch_spi_filter, param);
> +	if (!chan) {
> +		dev_err(&data->master->dev,
> +			"ERROR: dma_request_channel FAILS(Rx)\n");
> +		dma_release_channel(dma->chan_tx);
> +		dma->chan_tx = NULL;
> +		data->use_dma = 0;
> +		return;
> +	}
> +	dma->chan_rx = chan;
> +}
> +
> +static void pch_spi_release_dma(struct pch_spi_data *data)
> +{
> +	struct pch_spi_dma_ctrl *dma;
> +
> +	dma = &data->dma;
> +	if (dma->chan_tx) {
> +		dma_release_channel(dma->chan_tx);
> +		dma->chan_tx = NULL;
> +	}
> +	if (dma->chan_rx) {
> +		dma_release_channel(dma->chan_rx);
> +		dma->chan_rx = NULL;
> +	}
> +	return;
> +}
> +
> +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;
> +	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);
> +
> +	/* 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++;
> +		}
> +	}
> +	if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
> +		num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
> +		size = PCH_DMA_TRANS_SIZE;
> +		rem = data->bpw_len % 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_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
> +			    SPCR_TFIC_FIELD),
> +			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
> +
> +	spin_unlock_irqrestore(&data->lock, flags);
> +
> +	/* RX */
> +	dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
> +	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 == 0) {
> +			sg->offset = 0;
> +			sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
> +				    sg->offset);
> +			sg_dma_len(sg) = rem;
> +		} else {
> +			sg->offset = rem + size * (i - 1);
> +			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 = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
> +					num, DMA_FROM_DEVICE,
> +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc_rx) {
> +		dev_err(&data->master->dev, "%s:device_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;
> +
> +	/* TX */
> +	dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
> +	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), rem,
> +				    sg->offset);
> +			sg_dma_len(sg) = rem;
> +		} else {
> +			sg->offset = rem + size * (i - 1);
> +			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 = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
> +					sg, num, DMA_TO_DEVICE,
> +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc_tx) {
> +		dev_err(&data->master->dev, "%s:device_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, "\n%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;
> +}
> +
>  static void pch_spi_process_messages(struct work_struct *pwork)
>  {
>  	struct spi_message *pmsg;
> @@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
>  	dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
>  
>  	spin_lock(&data->lock);
> -
>  	/* check if suspend has been initiated;if yes flush queue */
>  	if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
> -		dev_dbg(&data->master->dev,
> -			"%s suspend/remove initiated,flushing queue\n",
> -			__func__);
> -
> +		dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
> +			"flushing queue\n", __func__);
>  		list_for_each_entry(pmsg, data->queue.next, queue) {
>  			pmsg->status = -EIO;
> -
>  			if (pmsg->complete != 0) {
>  				spin_unlock(&data->lock);
>  				pmsg->complete(pmsg->context);
>  				spin_lock(&data->lock);
>  			}
> -
>  			/* delete from queue */
>  			list_del_init(&pmsg->queue);
>  		}
> -
>  		spin_unlock(&data->lock);
>  		return;
>  	}
> -
>  	data->bcurrent_msg_processing = true;
>  	dev_dbg(&data->master->dev,
>  		"%s Set data->bcurrent_msg_processing= true\n", __func__);
> @@ -803,62 +1142,47 @@ static void pch_spi_process_messages(struct work_struct *pwork)
>  	/* Get the message from the queue and delete it from there. */
>  	data->current_msg = list_entry(data->queue.next, struct spi_message,
>  					queue);
> -
>  	list_del_init(&data->current_msg->queue);
> -
>  	data->current_msg->status = 0;
> -
>  	pch_spi_select_chip(data, data->current_msg->spi);
> -
>  	spin_unlock(&data->lock);
>  
> +	if (data->use_dma)
> +		pch_spi_request_dma(data,
> +				    data->current_msg->spi->bits_per_word);
>  	do {
>  		/* If we are already processing a message get the next
>  		transfer structure from the message otherwise retrieve
>  		the 1st transfer request from the message. */
>  		spin_lock(&data->lock);
> -
>  		if (data->cur_trans == NULL) {
>  			data->cur_trans =
> -			    list_entry(data->current_msg->transfers.
> -				       next, struct spi_transfer,
> -				       transfer_list);
> -			dev_dbg(&data->master->dev,
> -				"%s :Getting 1st transfer message\n", __func__);
> +				list_entry(data->current_msg->transfers.next,
> +					   struct spi_transfer, transfer_list);
> +			dev_dbg(&data->master->dev, "%s "
> +				":Getting 1st transfer message\n", __func__);
>  		} else {
>  			data->cur_trans =
> -			    list_entry(data->cur_trans->transfer_list.next,
> -				       struct spi_transfer,
> -				       transfer_list);
> -			dev_dbg(&data->master->dev,
> -				"%s :Getting next transfer message\n",
> -				__func__);
> +				list_entry(data->cur_trans->transfer_list.next,
> +					   struct spi_transfer, transfer_list);
> +			dev_dbg(&data->master->dev, "%s "
> +				":Getting next transfer message\n", __func__);
>  		}
> -
>  		spin_unlock(&data->lock);
>  
> -		pch_spi_set_tx(data, &bpw, &pmsg);
> -
> -		/* Control interrupt*/
> -		pch_spi_set_ir(data);
> -
> -		/* Disable SPI transfer */
> -		pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
> -				   SPCR_SPE_BIT);
> -
> -		/* clear FIFO */
> -		pch_spi_clear_fifo(data->master);
> -
> -		/* copy Rx Data */
> -		pch_spi_copy_rx_data(data, bpw);
> -
> -		/* free memory */
> -		kfree(data->pkt_rx_buff);
> -		data->pkt_rx_buff = NULL;
> -
> -		kfree(data->pkt_tx_buff);
> -		data->pkt_tx_buff = NULL;
> -
> +		if (data->use_dma) {
> +			pch_spi_handle_dma(data, &bpw);
> +			pch_spi_start_transfer(data);
> +			pch_spi_copy_rx_data_for_dma(data, bpw);
> +		} else {
> +			pch_spi_set_tx(data, &bpw);
> +			pch_spi_set_ir(data);
> +			pch_spi_copy_rx_data(data, bpw);
> +			kfree(data->pkt_rx_buff);
> +			data->pkt_rx_buff = NULL;
> +			kfree(data->pkt_tx_buff);
> +			data->pkt_tx_buff = NULL;
> +		}
>  		/* increment message count */
>  		data->current_msg->actual_length += data->cur_trans->len;
>  
> @@ -875,16 +1199,17 @@ static void pch_spi_process_messages(struct work_struct *pwork)
>  		}
>  
>  		spin_lock(&data->lock);
> -
>  		/* No more transfer in this message. */
>  		if ((data->cur_trans->transfer_list.next) ==
>  		    &(data->current_msg->transfers)) {
> -			pch_spi_nomore_transfer(data, pmsg);
> +			pch_spi_nomore_transfer(data);
>  		}
> -
>  		spin_unlock(&data->lock);
>  
>  	} while (data->cur_trans != NULL);
> +
> +	if (data->use_dma)
> +		pch_spi_release_dma(data);
>  }
>  
>  static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
> @@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
>  
>  	/* create workqueue */
>  	data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
> +
>  	if (!data->wk) {
>  		dev_err(&board_dat->pdev->dev,
>  			"%s create_singlet hread_workqueue failed\n", __func__);
> @@ -937,6 +1263,35 @@ err_return:
>  	return retval;
>  }
>  
> +static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
> +			     struct pch_spi_data *data)
> +{
> +	struct pch_spi_dma_ctrl *dma;
> +
> +	dma = &data->dma;
> +	if (dma->tx_buf_dma)
> +		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
> +				  dma->tx_buf_virt, dma->tx_buf_dma);
> +	if (dma->rx_buf_dma)
> +		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
> +				  dma->rx_buf_virt, dma->rx_buf_dma);
> +	return;
> +}
> +
> +static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
> +			      struct pch_spi_data *data)
> +{
> +	struct pch_spi_dma_ctrl *dma;
> +
> +	dma = &data->dma;
> +	/* Get Consistent memory for Tx DMA */
> +	dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
> +				PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
> +	/* Get Consistent memory for Rx DMA */
> +	dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
> +				PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
> +}
> +
>  static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  {
>  	int ret;
> @@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
>  	struct pch_spi_data *data;
>  
> +	dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
> +
>  	master = spi_alloc_master(&board_dat->pdev->dev,
>  				  sizeof(struct pch_spi_data));
>  	if (!master) {
> @@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  
>  	platform_set_drvdata(plat_dev, data);
>  
> -	/* baseaddress + 0x20(offset) */
> +	/* baseaddress + address offset) */
> +	data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
> +					 PCH_ADDRESS_SIZE * plat_dev->id;
>  	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
> -						   0x20 * plat_dev->id;
> +					 PCH_ADDRESS_SIZE * plat_dev->id;
>  	if (!data->io_remap_addr) {
>  		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
>  		ret = -ENOMEM;
> @@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  	data->n_curnt_chip = 255;
>  	data->status = STATUS_RUNNING;
>  	data->ch = plat_dev->id;
> +	data->use_dma = use_dma;
>  
>  	INIT_LIST_HEAD(&data->queue);
>  	spin_lock_init(&data->lock);
> @@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
>  		goto err_spi_register_master;
>  	}
>  
> +	if (use_dma) {
> +		dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
> +		pch_alloc_dma_buf(board_dat, data);
> +	}
> +
>  	return 0;
>  
>  err_spi_register_master:
> @@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
>  	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
>  	struct pch_spi_data *data = platform_get_drvdata(plat_dev);
>  	int count;
> +	unsigned long flags;
>  
>  	dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
>  		__func__, plat_dev->id, board_dat->pdev->irq);
> +
> +	if (use_dma)
> +		pch_free_dma_buf(board_dat, data);
> +
>  	/* check for any pending messages; no action is taken if the queue
>  	 * is still full; but at least we tried.  Unload anyway */
>  	count = 500;
> -	spin_lock(&data->lock);
> +	spin_lock_irqsave(&data->lock, flags);
>  	data->status = STATUS_EXITING;
>  	while ((list_empty(&data->queue) == 0) && --count) {
>  		dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
>  			__func__);
> -		spin_unlock(&data->lock);
> +		spin_unlock_irqrestore(&data->lock, flags);
>  		msleep(PCH_SLEEP_TIME);
> -		spin_lock(&data->lock);
> +		spin_lock_irqsave(&data->lock, flags);
>  	}
> -	spin_unlock(&data->lock);
> +	spin_unlock_irqrestore(&data->lock, flags);
>  
>  	pch_spi_free_resources(board_dat, data);
>  	/* disable interrupts & free IRQ */
> @@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
>  	/* check if the current message is processed:
>  	   Only after thats done the transfer will be suspended */
>  	count = 255;
> -	while ((--count) > 0)
> -		if (!(data->bcurrent_msg_processing)) {
> +	while ((--count) > 0) {
> +		if (!(data->bcurrent_msg_processing))
>  			break;
>  		msleep(PCH_SLEEP_TIME);
>  	}
> @@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
>  				"%s request_irq failed\n", __func__);
>  			return retval;
>  		}
> -
>  		/* reset PCH SPI h/w */
>  		pch_spi_reset(data->master);
>  		pch_spi_set_master_mode(data->master);
> @@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
>  	}
>  
>  	pci_set_drvdata(pdev, pd_dev_save);
> -
>  	return 0;
>  
>  err_platform_device:
> @@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
>  }
>  module_exit(pch_spi_exit);
>  
> +module_param(use_dma, int, 0644);
> +MODULE_PARM_DESC(use_dma,
> +		 "to use DMA for data transfers pass 1 else 0; default 1");
> +
>  MODULE_LICENSE("GPL");
>  MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
> -- 
> 1.7.4
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH
  2011-06-08 14:44   ` Grant Likely
@ 2011-06-09  1:04     ` Tomoya MORINAGA
  0 siblings, 0 replies; 9+ messages in thread
From: Tomoya MORINAGA @ 2011-06-09  1:04 UTC (permalink / raw)
  To: Grant Likely
  Cc: David Brownell, spi-devel-general, linux-kernel, qi.wang,
	yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux

(2011/06/08 23:44), Grant Likely wrote:

> Sorry about that Tomoya.  I got completely overloaded at the end of
> April and all of May.  I ended up dropping a lot of patches on the
> floor because of it.  I'm not purposefully ignoring you!

I can understand you ware very busy last month.
Though I thought you tried to avoid me,
I was relieved to read this your mail.

I wanted to know the information at last month.

Thanks,

tomoya

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support
@ 2009-01-01 23:29 y
  0 siblings, 0 replies; 9+ messages in thread
From: y @ 2009-01-01 23:29 UTC (permalink / raw)
  To: David Brownell, Grant Likely, spi-devel-general, linux-kernel
  Cc: qi.wang, yong.y.wang, joel.clark, kok.howg.ewe, toshiharu-linux,
	Tomoya MORINAGA

From: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>

This patch enables this SPI driver works with DMA mode.

Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
---
 drivers/spi/spi_topcliff_pch.c |  612 ++++++++++++++++++++++++++++++++--------
 1 files changed, 492 insertions(+), 120 deletions(-)

diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 88bd472..f543ff6 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -28,6 +28,9 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
 /* Register offsets */
 #define PCH_SPCR		0x00	/* SPI control register */
 #define PCH_SPBRR		0x04	/* SPI baud rate register */
@@ -36,7 +39,7 @@
 #define PCH_SPDRR		0x10	/* SPI read data register */
 #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */
 #define PCH_SRST		0x1C	/* SPI reset register */
-#define PCH_SPI_ADDRESS_SIZE	0x20
+#define PCH_ADDRESS_SIZE	0x20
 
 #define PCH_SPSR_TFD		0x000007C0
 #define PCH_SPSR_RFD		0x0000F800
@@ -54,8 +57,6 @@
 #define STATUS_EXITING		2
 #define PCH_SLEEP_TIME		10
 
-#define PCH_ADDRESS_SIZE	0x20
-
 #define SSN_LOW			0x02U
 #define SSN_NO_CONTROL		0x00U
 #define PCH_MAX_CS		0xFF
@@ -75,6 +76,7 @@
 #define SPSR_TFI_BIT		(1 << 0)
 #define SPSR_RFI_BIT		(1 << 1)
 #define SPSR_FI_BIT		(1 << 2)
+#define SPSR_ORF_BIT		(1 << 3)
 #define SPBRR_SIZE_BIT		(1 << 10)
 
 #define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
@@ -83,10 +85,9 @@
 #define SPCR_RFIC_FIELD		20
 #define SPCR_TFIC_FIELD		16
 
-#define SPSR_INT_BITS		0x1F
-#define MASK_SPBRR_SPBR_BITS	(~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS	(~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS	(~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS	((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS	(0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS	(0xf << SPCR_TFIC_FIELD)
 
 #define PCH_CLOCK_HZ		50000000
 #define PCH_MAX_SPBR		1023
@@ -102,6 +103,28 @@
 */
 #define PCH_SPI_MAX_DEV			2
 
+#define PCH_BUF_SIZE		4096
+#define PCH_DMA_TRANS_SIZE	12
+
+static int use_dma = 1;
+
+struct pch_spi_dma_ctrl {
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+	struct dma_chan		*chan_tx;
+	struct dma_chan		*chan_rx;
+	struct scatterlist		*sg_tx_p;
+	struct scatterlist		*sg_rx_p;
+	struct scatterlist		sg_tx;
+	struct scatterlist		sg_rx;
+	int				nent;
+	void				*tx_buf_virt;
+	void				*rx_buf_virt;
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+};
 /**
  * struct pch_spi_data - Holds the SPI channel specific details
  * @io_remap_addr:		The remapped PCI base address
@@ -140,6 +163,7 @@
  */
 struct pch_spi_data {
 	void __iomem *io_remap_addr;
+	unsigned long io_base_addr;
 	struct spi_master *master;
 	struct work_struct work;
 	struct workqueue_struct *wk;
@@ -162,6 +186,8 @@ struct pch_spi_data {
 	struct pch_spi_board_data *board_dat;
 	struct platform_device	*plat_dev;
 	int ch;
+	struct pch_spi_dma_ctrl dma;
+	int use_dma;
 	u8 irq_reg_sts;
 };
 
@@ -273,10 +299,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 			reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
 
 			/* reset rx threshold */
-			reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+			reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
 			reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
-			iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
-				 (io_remap_addr + PCH_SPCR));
+
+			iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
 		}
 
 		/* update counts */
@@ -287,12 +313,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 
 	/* if transfer complete interrupt */
 	if (reg_spsr_val & SPSR_FI_BIT) {
-		/* disable FI & RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
-				   SPCR_FIE_BIT | SPCR_RFIE_BIT);
+		if (tx_index < bpw_len)
+			dev_err(&data->master->dev,
+				"%s : Transfer is not completed", __func__);
+		/* 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);
 	}
 }
@@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
 			"%s returning due to suspend\n", __func__);
 		return IRQ_NONE;
 	}
+	if (data->use_dma)
+		return IRQ_NONE;
 
 	io_remap_addr = data->io_remap_addr;
 	spsr = io_remap_addr + PCH_SPSR;
 	reg_spsr_val = ioread32(spsr);
 
+	if (reg_spsr_val & SPSR_ORF_BIT)
+		dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
 	/* Check if the interrupt is for SPI device */
 	if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
 		pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
 	if (n_spbr > PCH_MAX_SPBR)
 		n_spbr = PCH_MAX_SPBR;
 
-	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
 }
 
 /**
@@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s Transfer List not empty. "
 		"Transfer Speed is set.\n", __func__);
 
+	spin_lock_irqsave(&data->lock, flags);
 	/* validate Tx/Rx buffers and Transfer length */
 	list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
 		if (!transfer->tx_buf && !transfer->rx_buf) {
 			dev_err(&pspi->dev,
 				"%s Tx and Rx buffer NULL\n", __func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		if (!transfer->len) {
 			dev_err(&pspi->dev, "%s Transfer length invalid\n",
 				__func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
 			" valid\n", __func__);
 
-		/* if baud rate hs been specified validate the same */
+		/* if baud rate has been specified validate the same */
 		if (transfer->speed_hz > PCH_MAX_BAUDRATE)
 			transfer->speed_hz = PCH_MAX_BAUDRATE;
 
@@ -484,25 +519,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 				retval = -EINVAL;
 				dev_err(&pspi->dev,
 					"%s Invalid bits per word\n", __func__);
-				goto err_out;
+				goto err_return_spinlock;
 			}
 		}
 	}
-
-	spin_lock_irqsave(&data->lock, flags);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	/* We won't process any messages if we have been asked to terminate */
 	if (data->status == STATUS_EXITING) {
 		dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
 		retval = -ESHUTDOWN;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* If suspended ,return -EINVAL */
 	if (data->board_dat->suspend_sts) {
 		dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
 		retval = -EINVAL;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* set status of message */
@@ -510,9 +544,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
 
 	pmsg->status = -EINPROGRESS;
-
+	spin_lock_irqsave(&data->lock, flags);
 	/* add message to queue */
 	list_add_tail(&pmsg->queue, &data->queue);
+	spin_unlock_irqrestore(&data->lock, flags);
+
 	dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
 
 	/* schedule work queue to run */
@@ -521,11 +557,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 
 	retval = 0;
 
-err_return_spinlock:
-	spin_unlock_irqrestore(&data->lock, flags);
 err_out:
 	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
 	return retval;
+err_return_spinlock:
+	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return retval;
 }
 
 static inline void pch_spi_select_chip(struct pch_spi_data *data,
@@ -546,8 +584,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
 	pch_spi_setup_transfer(pspi);
 }
 
-static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
-			   struct spi_message **ppmsg)
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
 {
 	int size;
 	u32 n_writes;
@@ -556,8 +593,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	const u8 *tx_buf;
 	const u16 *tx_sbuf;
 
-	pmsg = *ppmsg;
-
 	/* set baud rate if needed */
 	if (data->cur_trans->speed_hz) {
 		dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
@@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 
 	/* 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)) {
+	    (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__);
 		pch_spi_set_bits_per_word(data->master,
 					  data->cur_trans->bits_per_word);
@@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	data->transfer_active = true;
 }
 
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
-						struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
 {
+	struct spi_message *pmsg;
 	dev_dbg(&data->master->dev, "%s called\n", __func__);
 	/* Invoke complete callback
 	 * [To the spi core..indicating end of transfer] */
@@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
 
 static void pch_spi_set_ir(struct pch_spi_data *data)
 {
-	/* enable interrupts */
-	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+	/* enable interrupts, set threshold, enable SPI */
+	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
 		/* set receive threshold to PCH_RX_THOLD */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD << SPCR_RFIC_FIELD,
-				   ~MASK_RFIC_SPCR_BITS);
-		/* enable FI and RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
-	} else {
+				   PCH_RX_THOLD << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_RFIE_BIT |
+				   SPCR_ORIE_BIT | SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
+	else
 		/* set receive threshold to maximum */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
-				   ~MASK_TFIC_SPCR_BITS);
-		/* enable FI interrupt */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
-	}
-
-	dev_dbg(&data->master->dev,
-		"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
-	/* SPI set enable */
-	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
-			   0);
+				   PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_ORIE_BIT |
+				   SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
 
 	/* Wait until the transfer completes; go to sleep after
 				 initiating the transfer. */
@@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
 	dev_dbg(&data->master->dev,
 		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
 
-	data->transfer_active = false;
-	dev_dbg(&data->master->dev,
-		"%s set data->transfer_active = false\n", __func__);
-
 	/* clear all interrupts */
 	pch_spi_writereg(data->master, PCH_SPSR,
 			 pch_spi_readreg(data->master, PCH_SPSR));
-	/* disable interrupts */
-	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+	/* Disable interrupts and SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
 }
 
 static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
@@ -762,6 +786,328 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
 	}
 }
 
+static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
+{
+	int j;
+	u8 *rx_buf;
+	u16 *rx_sbuf;
+	const u8 *rx_dma_buf;
+	const u16 *rx_dma_sbuf;
+
+	/* copy Rx Data */
+	if (!data->cur_trans->rx_buf)
+		return;
+
+	if (bpw == 8) {
+		rx_buf = data->cur_trans->rx_buf;
+		rx_dma_buf = data->dma.rx_buf_virt;
+		for (j = 0; j < data->bpw_len; j++)
+			*rx_buf++ = *rx_dma_buf++ & 0xFF;
+	} 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++;
+	}
+}
+
+static void pch_spi_start_transfer(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+	unsigned long flags;
+
+	dma = &data->dma;
+
+	spin_lock_irqsave(&data->lock, flags);
+
+	/* disable interrupts, SPI set enable */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* Wait until the transfer completes; go to sleep after
+				 initiating the transfer. */
+	dev_dbg(&data->master->dev,
+		"%s:waiting for transfer to get over\n", __func__);
+	wait_event_interruptible(data->wait, data->transfer_complete);
+
+	dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
+			    DMA_FROM_DEVICE);
+	async_tx_ack(dma->desc_rx);
+	async_tx_ack(dma->desc_tx);
+	kfree(dma->sg_tx_p);
+	kfree(dma->sg_rx_p);
+
+	spin_lock_irqsave(&data->lock, flags);
+	pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+	dev_dbg(&data->master->dev,
+		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+	/* clear fifo threshold, disable interrupts, disable SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
+			   SPCR_SPE_BIT);
+	/* clear all interrupts */
+	pch_spi_writereg(data->master, PCH_SPSR,
+			 pch_spi_readreg(data->master, PCH_SPSR));
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+	struct pch_spi_data *data = arg;
+
+	/* transfer is completed;inform pch_spi_process_messages_dma */
+	data->transfer_complete = true;
+	wake_up_interruptible(&data->wait);
+}
+
+static bool pch_spi_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) &&
+	    (param->dma_dev == chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	struct pch_dma_slave *param;
+	struct pch_spi_dma_ctrl *dma;
+	unsigned int width;
+
+	if (bpw == 8)
+		width = PCH_DMA_WIDTH_1_BYTE;
+	else
+		width = PCH_DMA_WIDTH_2_BYTES;
+
+	dma = &data->dma;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/* Get DMA's dev information */
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+
+	/* Set Tx DMA */
+	param = &dma->param_tx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
+	param->tx_reg = data->io_base_addr + PCH_SPDWR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Tx)\n");
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_tx = chan;
+
+	/* Set Rx DMA */
+	param = &dma->param_rx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
+	param->rx_reg = data->io_base_addr + PCH_SPDRR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Rx)\n");
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_rx = chan;
+}
+
+static void pch_spi_release_dma(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->chan_tx) {
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+	}
+	if (dma->chan_rx) {
+		dma_release_channel(dma->chan_rx);
+		dma->chan_rx = NULL;
+	}
+	return;
+}
+
+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;
+	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);
+
+	/* 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++;
+		}
+	}
+	if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
+		num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+		size = PCH_DMA_TRANS_SIZE;
+		rem = data->bpw_len % 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_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
+			    SPCR_TFIC_FIELD),
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* RX */
+	dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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 == 0) {
+			sg->offset = 0;
+			sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+					num, DMA_FROM_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_rx) {
+		dev_err(&data->master->dev, "%s:device_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;
+
+	/* TX */
+	dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+					sg, num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_tx) {
+		dev_err(&data->master->dev, "%s:device_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, "\n%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;
+}
+
 static void pch_spi_process_messages(struct work_struct *pwork)
 {
 	struct spi_message *pmsg;
@@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
 
 	spin_lock(&data->lock);
-
 	/* check if suspend has been initiated;if yes flush queue */
 	if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
-		dev_dbg(&data->master->dev,
-			"%s suspend/remove initiated,flushing queue\n",
-			__func__);
-
+		dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
+			"flushing queue\n", __func__);
 		list_for_each_entry(pmsg, data->queue.next, queue) {
 			pmsg->status = -EIO;
-
 			if (pmsg->complete != 0) {
 				spin_unlock(&data->lock);
 				pmsg->complete(pmsg->context);
 				spin_lock(&data->lock);
 			}
-
 			/* delete from queue */
 			list_del_init(&pmsg->queue);
 		}
-
 		spin_unlock(&data->lock);
 		return;
 	}
-
 	data->bcurrent_msg_processing = true;
 	dev_dbg(&data->master->dev,
 		"%s Set data->bcurrent_msg_processing= true\n", __func__);
@@ -803,62 +1142,47 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	/* Get the message from the queue and delete it from there. */
 	data->current_msg = list_entry(data->queue.next, struct spi_message,
 					queue);
-
 	list_del_init(&data->current_msg->queue);
-
 	data->current_msg->status = 0;
-
 	pch_spi_select_chip(data, data->current_msg->spi);
-
 	spin_unlock(&data->lock);
 
+	if (data->use_dma)
+		pch_spi_request_dma(data,
+				    data->current_msg->spi->bits_per_word);
 	do {
 		/* If we are already processing a message get the next
 		transfer structure from the message otherwise retrieve
 		the 1st transfer request from the message. */
 		spin_lock(&data->lock);
-
 		if (data->cur_trans == NULL) {
 			data->cur_trans =
-			    list_entry(data->current_msg->transfers.
-				       next, struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting 1st transfer message\n", __func__);
+				list_entry(data->current_msg->transfers.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting 1st transfer message\n", __func__);
 		} else {
 			data->cur_trans =
-			    list_entry(data->cur_trans->transfer_list.next,
-				       struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting next transfer message\n",
-				__func__);
+				list_entry(data->cur_trans->transfer_list.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting next transfer message\n", __func__);
 		}
-
 		spin_unlock(&data->lock);
 
-		pch_spi_set_tx(data, &bpw, &pmsg);
-
-		/* Control interrupt*/
-		pch_spi_set_ir(data);
-
-		/* Disable SPI transfer */
-		pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
-				   SPCR_SPE_BIT);
-
-		/* clear FIFO */
-		pch_spi_clear_fifo(data->master);
-
-		/* copy Rx Data */
-		pch_spi_copy_rx_data(data, bpw);
-
-		/* free memory */
-		kfree(data->pkt_rx_buff);
-		data->pkt_rx_buff = NULL;
-
-		kfree(data->pkt_tx_buff);
-		data->pkt_tx_buff = NULL;
-
+		if (data->use_dma) {
+			pch_spi_handle_dma(data, &bpw);
+			pch_spi_start_transfer(data);
+			pch_spi_copy_rx_data_for_dma(data, bpw);
+		} else {
+			pch_spi_set_tx(data, &bpw);
+			pch_spi_set_ir(data);
+			pch_spi_copy_rx_data(data, bpw);
+			kfree(data->pkt_rx_buff);
+			data->pkt_rx_buff = NULL;
+			kfree(data->pkt_tx_buff);
+			data->pkt_tx_buff = NULL;
+		}
 		/* increment message count */
 		data->current_msg->actual_length += data->cur_trans->len;
 
@@ -875,16 +1199,17 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 		}
 
 		spin_lock(&data->lock);
-
 		/* No more transfer in this message. */
 		if ((data->cur_trans->transfer_list.next) ==
 		    &(data->current_msg->transfers)) {
-			pch_spi_nomore_transfer(data, pmsg);
+			pch_spi_nomore_transfer(data);
 		}
-
 		spin_unlock(&data->lock);
 
 	} while (data->cur_trans != NULL);
+
+	if (data->use_dma)
+		pch_spi_release_dma(data);
 }
 
 static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
@@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
 
 	/* create workqueue */
 	data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+
 	if (!data->wk) {
 		dev_err(&board_dat->pdev->dev,
 			"%s create_singlet hread_workqueue failed\n", __func__);
@@ -937,6 +1263,35 @@ err_return:
 	return retval;
 }
 
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+			     struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->tx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->tx_buf_virt, dma->tx_buf_dma);
+	if (dma->rx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->rx_buf_virt, dma->rx_buf_dma);
+	return;
+}
+
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+			      struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	/* Get Consistent memory for Tx DMA */
+	dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+	/* Get Consistent memory for Rx DMA */
+	dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
+
 static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 {
 	int ret;
@@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data;
 
+	dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
+
 	master = spi_alloc_master(&board_dat->pdev->dev,
 				  sizeof(struct pch_spi_data));
 	if (!master) {
@@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 
 	platform_set_drvdata(plat_dev, data);
 
-	/* baseaddress + 0x20(offset) */
+	/* baseaddress + address offset) */
+	data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
-						   0x20 * plat_dev->id;
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	if (!data->io_remap_addr) {
 		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
 		ret = -ENOMEM;
@@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	data->n_curnt_chip = 255;
 	data->status = STATUS_RUNNING;
 	data->ch = plat_dev->id;
+	data->use_dma = use_dma;
 
 	INIT_LIST_HEAD(&data->queue);
 	spin_lock_init(&data->lock);
@@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 		goto err_spi_register_master;
 	}
 
+	if (use_dma) {
+		dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+		pch_alloc_dma_buf(board_dat, data);
+	}
+
 	return 0;
 
 err_spi_register_master:
@@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data = platform_get_drvdata(plat_dev);
 	int count;
+	unsigned long flags;
 
 	dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
 		__func__, plat_dev->id, board_dat->pdev->irq);
+
+	if (use_dma)
+		pch_free_dma_buf(board_dat, data);
+
 	/* check for any pending messages; no action is taken if the queue
 	 * is still full; but at least we tried.  Unload anyway */
 	count = 500;
-	spin_lock(&data->lock);
+	spin_lock_irqsave(&data->lock, flags);
 	data->status = STATUS_EXITING;
 	while ((list_empty(&data->queue) == 0) && --count) {
 		dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
 			__func__);
-		spin_unlock(&data->lock);
+		spin_unlock_irqrestore(&data->lock, flags);
 		msleep(PCH_SLEEP_TIME);
-		spin_lock(&data->lock);
+		spin_lock_irqsave(&data->lock, flags);
 	}
-	spin_unlock(&data->lock);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	pch_spi_free_resources(board_dat, data);
 	/* disable interrupts & free IRQ */
@@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
 	/* check if the current message is processed:
 	   Only after thats done the transfer will be suspended */
 	count = 255;
-	while ((--count) > 0)
-		if (!(data->bcurrent_msg_processing)) {
+	while ((--count) > 0) {
+		if (!(data->bcurrent_msg_processing))
 			break;
 		msleep(PCH_SLEEP_TIME);
 	}
@@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
 				"%s request_irq failed\n", __func__);
 			return retval;
 		}
-
 		/* reset PCH SPI h/w */
 		pch_spi_reset(data->master);
 		pch_spi_set_master_mode(data->master);
@@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
 	}
 
 	pci_set_drvdata(pdev, pd_dev_save);
-
 	return 0;
 
 err_platform_device:
@@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
 }
 module_exit(pch_spi_exit);
 
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+		 "to use DMA for data transfers pass 1 else 0; default 1");
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
-- 
1.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support
@ 2009-01-01 23:29 Unknown, y
  0 siblings, 0 replies; 9+ messages in thread
From: Unknown, y @ 2009-01-01 23:29 UTC (permalink / raw)
  To: David Brownell, Grant Likely,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: qi.wang-ral2JQCrhuEAvxtiuMwx3w,
	yong.y.wang-ral2JQCrhuEAvxtiuMwx3w, Tomoya MORINAGA,
	toshiharu-linux-ECg8zkTtlr0C6LszWs/t0g,
	kok.howg.ewe-ral2JQCrhuEAvxtiuMwx3w,
	joel.clark-ral2JQCrhuEAvxtiuMwx3w

From: Tomoya MORINAGA <tomoya-linux-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>

This patch enables this SPI driver works with DMA mode.

Signed-off-by: Tomoya MORINAGA <tomoya-linux-ECg8zkTtlr0C6LszWs/t0g@public.gmane.org>
---
 drivers/spi/spi_topcliff_pch.c |  612 ++++++++++++++++++++++++++++++++--------
 1 files changed, 492 insertions(+), 120 deletions(-)

diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
index 88bd472..f543ff6 100644
--- a/drivers/spi/spi_topcliff_pch.c
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -28,6 +28,9 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
 /* Register offsets */
 #define PCH_SPCR		0x00	/* SPI control register */
 #define PCH_SPBRR		0x04	/* SPI baud rate register */
@@ -36,7 +39,7 @@
 #define PCH_SPDRR		0x10	/* SPI read data register */
 #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */
 #define PCH_SRST		0x1C	/* SPI reset register */
-#define PCH_SPI_ADDRESS_SIZE	0x20
+#define PCH_ADDRESS_SIZE	0x20
 
 #define PCH_SPSR_TFD		0x000007C0
 #define PCH_SPSR_RFD		0x0000F800
@@ -54,8 +57,6 @@
 #define STATUS_EXITING		2
 #define PCH_SLEEP_TIME		10
 
-#define PCH_ADDRESS_SIZE	0x20
-
 #define SSN_LOW			0x02U
 #define SSN_NO_CONTROL		0x00U
 #define PCH_MAX_CS		0xFF
@@ -75,6 +76,7 @@
 #define SPSR_TFI_BIT		(1 << 0)
 #define SPSR_RFI_BIT		(1 << 1)
 #define SPSR_FI_BIT		(1 << 2)
+#define SPSR_ORF_BIT		(1 << 3)
 #define SPBRR_SIZE_BIT		(1 << 10)
 
 #define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\
@@ -83,10 +85,9 @@
 #define SPCR_RFIC_FIELD		20
 #define SPCR_TFIC_FIELD		16
 
-#define SPSR_INT_BITS		0x1F
-#define MASK_SPBRR_SPBR_BITS	(~((1 << 10) - 1))
-#define MASK_RFIC_SPCR_BITS	(~(0xf << 20))
-#define MASK_TFIC_SPCR_BITS	(~(0xf000f << 12))
+#define MASK_SPBRR_SPBR_BITS	((1 << 10) - 1)
+#define MASK_RFIC_SPCR_BITS	(0xf << SPCR_RFIC_FIELD)
+#define MASK_TFIC_SPCR_BITS	(0xf << SPCR_TFIC_FIELD)
 
 #define PCH_CLOCK_HZ		50000000
 #define PCH_MAX_SPBR		1023
@@ -102,6 +103,28 @@
 */
 #define PCH_SPI_MAX_DEV			2
 
+#define PCH_BUF_SIZE		4096
+#define PCH_DMA_TRANS_SIZE	12
+
+static int use_dma = 1;
+
+struct pch_spi_dma_ctrl {
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+	struct dma_chan		*chan_tx;
+	struct dma_chan		*chan_rx;
+	struct scatterlist		*sg_tx_p;
+	struct scatterlist		*sg_rx_p;
+	struct scatterlist		sg_tx;
+	struct scatterlist		sg_rx;
+	int				nent;
+	void				*tx_buf_virt;
+	void				*rx_buf_virt;
+	dma_addr_t			tx_buf_dma;
+	dma_addr_t			rx_buf_dma;
+};
 /**
  * struct pch_spi_data - Holds the SPI channel specific details
  * @io_remap_addr:		The remapped PCI base address
@@ -140,6 +163,7 @@
  */
 struct pch_spi_data {
 	void __iomem *io_remap_addr;
+	unsigned long io_base_addr;
 	struct spi_master *master;
 	struct work_struct work;
 	struct workqueue_struct *wk;
@@ -162,6 +186,8 @@ struct pch_spi_data {
 	struct pch_spi_board_data *board_dat;
 	struct platform_device	*plat_dev;
 	int ch;
+	struct pch_spi_dma_ctrl dma;
+	int use_dma;
 	u8 irq_reg_sts;
 };
 
@@ -273,10 +299,10 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 			reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
 
 			/* reset rx threshold */
-			reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+			reg_spcr_val &= ~MASK_RFIC_SPCR_BITS;
 			reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
-			iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
-				 (io_remap_addr + PCH_SPCR));
+
+			iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR));
 		}
 
 		/* update counts */
@@ -287,12 +313,15 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
 
 	/* if transfer complete interrupt */
 	if (reg_spsr_val & SPSR_FI_BIT) {
-		/* disable FI & RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
-				   SPCR_FIE_BIT | SPCR_RFIE_BIT);
+		if (tx_index < bpw_len)
+			dev_err(&data->master->dev,
+				"%s : Transfer is not completed", __func__);
+		/* 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);
 	}
 }
@@ -316,11 +345,16 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id)
 			"%s returning due to suspend\n", __func__);
 		return IRQ_NONE;
 	}
+	if (data->use_dma)
+		return IRQ_NONE;
 
 	io_remap_addr = data->io_remap_addr;
 	spsr = io_remap_addr + PCH_SPSR;
 	reg_spsr_val = ioread32(spsr);
 
+	if (reg_spsr_val & SPSR_ORF_BIT)
+		dev_err(&board_dat->pdev->dev, "%s Over run error", __func__);
+
 	/* Check if the interrupt is for SPI device */
 	if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
 		pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
@@ -346,7 +380,7 @@ static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
 	if (n_spbr > PCH_MAX_SPBR)
 		n_spbr = PCH_MAX_SPBR;
 
-	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+	pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS);
 }
 
 /**
@@ -454,26 +488,27 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s Transfer List not empty. "
 		"Transfer Speed is set.\n", __func__);
 
+	spin_lock_irqsave(&data->lock, flags);
 	/* validate Tx/Rx buffers and Transfer length */
 	list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
 		if (!transfer->tx_buf && !transfer->rx_buf) {
 			dev_err(&pspi->dev,
 				"%s Tx and Rx buffer NULL\n", __func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		if (!transfer->len) {
 			dev_err(&pspi->dev, "%s Transfer length invalid\n",
 				__func__);
 			retval = -EINVAL;
-			goto err_out;
+			goto err_return_spinlock;
 		}
 
 		dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
 			" valid\n", __func__);
 
-		/* if baud rate hs been specified validate the same */
+		/* if baud rate has been specified validate the same */
 		if (transfer->speed_hz > PCH_MAX_BAUDRATE)
 			transfer->speed_hz = PCH_MAX_BAUDRATE;
 
@@ -484,25 +519,24 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 				retval = -EINVAL;
 				dev_err(&pspi->dev,
 					"%s Invalid bits per word\n", __func__);
-				goto err_out;
+				goto err_return_spinlock;
 			}
 		}
 	}
-
-	spin_lock_irqsave(&data->lock, flags);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	/* We won't process any messages if we have been asked to terminate */
 	if (data->status == STATUS_EXITING) {
 		dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
 		retval = -ESHUTDOWN;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* If suspended ,return -EINVAL */
 	if (data->board_dat->suspend_sts) {
 		dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
 		retval = -EINVAL;
-		goto err_return_spinlock;
+		goto err_out;
 	}
 
 	/* set status of message */
@@ -510,9 +544,11 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 	dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
 
 	pmsg->status = -EINPROGRESS;
-
+	spin_lock_irqsave(&data->lock, flags);
 	/* add message to queue */
 	list_add_tail(&pmsg->queue, &data->queue);
+	spin_unlock_irqrestore(&data->lock, flags);
+
 	dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
 
 	/* schedule work queue to run */
@@ -521,11 +557,13 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
 
 	retval = 0;
 
-err_return_spinlock:
-	spin_unlock_irqrestore(&data->lock, flags);
 err_out:
 	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
 	return retval;
+err_return_spinlock:
+	dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return retval;
 }
 
 static inline void pch_spi_select_chip(struct pch_spi_data *data,
@@ -546,8 +584,7 @@ static inline void pch_spi_select_chip(struct pch_spi_data *data,
 	pch_spi_setup_transfer(pspi);
 }
 
-static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
-			   struct spi_message **ppmsg)
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
 {
 	int size;
 	u32 n_writes;
@@ -556,8 +593,6 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	const u8 *tx_buf;
 	const u16 *tx_sbuf;
 
-	pmsg = *ppmsg;
-
 	/* set baud rate if needed */
 	if (data->cur_trans->speed_hz) {
 		dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
@@ -566,8 +601,8 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 
 	/* 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)) {
+	    (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__);
 		pch_spi_set_bits_per_word(data->master,
 					  data->cur_trans->bits_per_word);
@@ -641,9 +676,9 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
 	data->transfer_active = true;
 }
 
-static void pch_spi_nomore_transfer(struct pch_spi_data *data,
-						struct spi_message *pmsg)
+static void pch_spi_nomore_transfer(struct pch_spi_data *data)
 {
+	struct spi_message *pmsg;
 	dev_dbg(&data->master->dev, "%s called\n", __func__);
 	/* Invoke complete callback
 	 * [To the spi core..indicating end of transfer] */
@@ -694,30 +729,21 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data,
 
 static void pch_spi_set_ir(struct pch_spi_data *data)
 {
-	/* enable interrupts */
-	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+	/* enable interrupts, set threshold, enable SPI */
+	if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH)
 		/* set receive threshold to PCH_RX_THOLD */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD << SPCR_RFIC_FIELD,
-				   ~MASK_RFIC_SPCR_BITS);
-		/* enable FI and RFI interrupts */
-		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   SPCR_RFIE_BIT | SPCR_FIE_BIT, 0);
-	} else {
+				   PCH_RX_THOLD << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_RFIE_BIT |
+				   SPCR_ORIE_BIT | SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
+	else
 		/* set receive threshold to maximum */
 		pch_spi_setclr_reg(data->master, PCH_SPCR,
-				   PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
-				   ~MASK_TFIC_SPCR_BITS);
-		/* enable FI interrupt */
-		pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
-	}
-
-	dev_dbg(&data->master->dev,
-		"%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
-
-	/* SPI set enable */
-	pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT,
-			   0);
+				   PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD |
+				   SPCR_FIE_BIT | SPCR_ORIE_BIT |
+				   SPCR_SPE_BIT,
+				   MASK_RFIC_SPCR_BITS | PCH_ALL);
 
 	/* Wait until the transfer completes; go to sleep after
 				 initiating the transfer. */
@@ -730,15 +756,13 @@ static void pch_spi_set_ir(struct pch_spi_data *data)
 	dev_dbg(&data->master->dev,
 		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
 
-	data->transfer_active = false;
-	dev_dbg(&data->master->dev,
-		"%s set data->transfer_active = false\n", __func__);
-
 	/* clear all interrupts */
 	pch_spi_writereg(data->master, PCH_SPSR,
 			 pch_spi_readreg(data->master, PCH_SPSR));
-	/* disable interrupts */
-	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+	/* Disable interrupts and SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT);
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
 }
 
 static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
@@ -762,6 +786,328 @@ static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
 	}
 }
 
+static void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw)
+{
+	int j;
+	u8 *rx_buf;
+	u16 *rx_sbuf;
+	const u8 *rx_dma_buf;
+	const u16 *rx_dma_sbuf;
+
+	/* copy Rx Data */
+	if (!data->cur_trans->rx_buf)
+		return;
+
+	if (bpw == 8) {
+		rx_buf = data->cur_trans->rx_buf;
+		rx_dma_buf = data->dma.rx_buf_virt;
+		for (j = 0; j < data->bpw_len; j++)
+			*rx_buf++ = *rx_dma_buf++ & 0xFF;
+	} 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++;
+	}
+}
+
+static void pch_spi_start_transfer(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+	unsigned long flags;
+
+	dma = &data->dma;
+
+	spin_lock_irqsave(&data->lock, flags);
+
+	/* disable interrupts, SPI set enable */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* Wait until the transfer completes; go to sleep after
+				 initiating the transfer. */
+	dev_dbg(&data->master->dev,
+		"%s:waiting for transfer to get over\n", __func__);
+	wait_event_interruptible(data->wait, data->transfer_complete);
+
+	dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent,
+			    DMA_FROM_DEVICE);
+	async_tx_ack(dma->desc_rx);
+	async_tx_ack(dma->desc_tx);
+	kfree(dma->sg_tx_p);
+	kfree(dma->sg_rx_p);
+
+	spin_lock_irqsave(&data->lock, flags);
+	pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+	dev_dbg(&data->master->dev,
+		"%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+	/* clear fifo threshold, disable interrupts, disable SPI transfer */
+	pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL |
+			   SPCR_SPE_BIT);
+	/* clear all interrupts */
+	pch_spi_writereg(data->master, PCH_SPSR,
+			 pch_spi_readreg(data->master, PCH_SPSR));
+	/* clear FIFO */
+	pch_spi_clear_fifo(data->master);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+	struct pch_spi_data *data = arg;
+
+	/* transfer is completed;inform pch_spi_process_messages_dma */
+	data->transfer_complete = true;
+	wake_up_interruptible(&data->wait);
+}
+
+static bool pch_spi_filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) &&
+	    (param->dma_dev == chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	struct pch_dma_slave *param;
+	struct pch_spi_dma_ctrl *dma;
+	unsigned int width;
+
+	if (bpw == 8)
+		width = PCH_DMA_WIDTH_1_BYTE;
+	else
+		width = PCH_DMA_WIDTH_2_BYTES;
+
+	dma = &data->dma;
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/* Get DMA's dev information */
+	dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(12, 0));
+
+	/* Set Tx DMA */
+	param = &dma->param_tx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */
+	param->tx_reg = data->io_base_addr + PCH_SPDWR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Tx)\n");
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_tx = chan;
+
+	/* Set Rx DMA */
+	param = &dma->param_rx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */
+	param->rx_reg = data->io_base_addr + PCH_SPDRR;
+	param->width = width;
+	chan = dma_request_channel(mask, pch_spi_filter, param);
+	if (!chan) {
+		dev_err(&data->master->dev,
+			"ERROR: dma_request_channel FAILS(Rx)\n");
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+		data->use_dma = 0;
+		return;
+	}
+	dma->chan_rx = chan;
+}
+
+static void pch_spi_release_dma(struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->chan_tx) {
+		dma_release_channel(dma->chan_tx);
+		dma->chan_tx = NULL;
+	}
+	if (dma->chan_rx) {
+		dma_release_channel(dma->chan_rx);
+		dma->chan_rx = NULL;
+	}
+	return;
+}
+
+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;
+	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);
+
+	/* 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++;
+		}
+	}
+	if (data->bpw_len > PCH_DMA_TRANS_SIZE) {
+		num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1;
+		size = PCH_DMA_TRANS_SIZE;
+		rem = data->bpw_len % 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_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE) <<
+			    SPCR_TFIC_FIELD),
+			   MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS);
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* RX */
+	dma->sg_rx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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 == 0) {
+			sg->offset = 0;
+			sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_rx->device->device_prep_slave_sg(dma->chan_rx, sg,
+					num, DMA_FROM_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_rx) {
+		dev_err(&data->master->dev, "%s:device_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;
+
+	/* TX */
+	dma->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+	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), rem,
+				    sg->offset);
+			sg_dma_len(sg) = rem;
+		} else {
+			sg->offset = rem + size * (i - 1);
+			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 = dma->chan_tx->device->device_prep_slave_sg(dma->chan_tx,
+					sg, num, DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc_tx) {
+		dev_err(&data->master->dev, "%s:device_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, "\n%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;
+}
+
 static void pch_spi_process_messages(struct work_struct *pwork)
 {
 	struct spi_message *pmsg;
@@ -772,30 +1118,23 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
 
 	spin_lock(&data->lock);
-
 	/* check if suspend has been initiated;if yes flush queue */
 	if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
-		dev_dbg(&data->master->dev,
-			"%s suspend/remove initiated,flushing queue\n",
-			__func__);
-
+		dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
+			"flushing queue\n", __func__);
 		list_for_each_entry(pmsg, data->queue.next, queue) {
 			pmsg->status = -EIO;
-
 			if (pmsg->complete != 0) {
 				spin_unlock(&data->lock);
 				pmsg->complete(pmsg->context);
 				spin_lock(&data->lock);
 			}
-
 			/* delete from queue */
 			list_del_init(&pmsg->queue);
 		}
-
 		spin_unlock(&data->lock);
 		return;
 	}
-
 	data->bcurrent_msg_processing = true;
 	dev_dbg(&data->master->dev,
 		"%s Set data->bcurrent_msg_processing= true\n", __func__);
@@ -803,62 +1142,47 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 	/* Get the message from the queue and delete it from there. */
 	data->current_msg = list_entry(data->queue.next, struct spi_message,
 					queue);
-
 	list_del_init(&data->current_msg->queue);
-
 	data->current_msg->status = 0;
-
 	pch_spi_select_chip(data, data->current_msg->spi);
-
 	spin_unlock(&data->lock);
 
+	if (data->use_dma)
+		pch_spi_request_dma(data,
+				    data->current_msg->spi->bits_per_word);
 	do {
 		/* If we are already processing a message get the next
 		transfer structure from the message otherwise retrieve
 		the 1st transfer request from the message. */
 		spin_lock(&data->lock);
-
 		if (data->cur_trans == NULL) {
 			data->cur_trans =
-			    list_entry(data->current_msg->transfers.
-				       next, struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting 1st transfer message\n", __func__);
+				list_entry(data->current_msg->transfers.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting 1st transfer message\n", __func__);
 		} else {
 			data->cur_trans =
-			    list_entry(data->cur_trans->transfer_list.next,
-				       struct spi_transfer,
-				       transfer_list);
-			dev_dbg(&data->master->dev,
-				"%s :Getting next transfer message\n",
-				__func__);
+				list_entry(data->cur_trans->transfer_list.next,
+					   struct spi_transfer, transfer_list);
+			dev_dbg(&data->master->dev, "%s "
+				":Getting next transfer message\n", __func__);
 		}
-
 		spin_unlock(&data->lock);
 
-		pch_spi_set_tx(data, &bpw, &pmsg);
-
-		/* Control interrupt*/
-		pch_spi_set_ir(data);
-
-		/* Disable SPI transfer */
-		pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
-				   SPCR_SPE_BIT);
-
-		/* clear FIFO */
-		pch_spi_clear_fifo(data->master);
-
-		/* copy Rx Data */
-		pch_spi_copy_rx_data(data, bpw);
-
-		/* free memory */
-		kfree(data->pkt_rx_buff);
-		data->pkt_rx_buff = NULL;
-
-		kfree(data->pkt_tx_buff);
-		data->pkt_tx_buff = NULL;
-
+		if (data->use_dma) {
+			pch_spi_handle_dma(data, &bpw);
+			pch_spi_start_transfer(data);
+			pch_spi_copy_rx_data_for_dma(data, bpw);
+		} else {
+			pch_spi_set_tx(data, &bpw);
+			pch_spi_set_ir(data);
+			pch_spi_copy_rx_data(data, bpw);
+			kfree(data->pkt_rx_buff);
+			data->pkt_rx_buff = NULL;
+			kfree(data->pkt_tx_buff);
+			data->pkt_tx_buff = NULL;
+		}
 		/* increment message count */
 		data->current_msg->actual_length += data->cur_trans->len;
 
@@ -875,16 +1199,17 @@ static void pch_spi_process_messages(struct work_struct *pwork)
 		}
 
 		spin_lock(&data->lock);
-
 		/* No more transfer in this message. */
 		if ((data->cur_trans->transfer_list.next) ==
 		    &(data->current_msg->transfers)) {
-			pch_spi_nomore_transfer(data, pmsg);
+			pch_spi_nomore_transfer(data);
 		}
-
 		spin_unlock(&data->lock);
 
 	} while (data->cur_trans != NULL);
+
+	if (data->use_dma)
+		pch_spi_release_dma(data);
 }
 
 static void pch_spi_free_resources(struct pch_spi_board_data *board_dat,
@@ -911,6 +1236,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat,
 
 	/* create workqueue */
 	data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+
 	if (!data->wk) {
 		dev_err(&board_dat->pdev->dev,
 			"%s create_singlet hread_workqueue failed\n", __func__);
@@ -937,6 +1263,35 @@ err_return:
 	return retval;
 }
 
+static void pch_free_dma_buf(struct pch_spi_board_data *board_dat,
+			     struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	if (dma->tx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->tx_buf_virt, dma->tx_buf_dma);
+	if (dma->rx_buf_dma)
+		dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE,
+				  dma->rx_buf_virt, dma->rx_buf_dma);
+	return;
+}
+
+static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
+			      struct pch_spi_data *data)
+{
+	struct pch_spi_dma_ctrl *dma;
+
+	dma = &data->dma;
+	/* Get Consistent memory for Tx DMA */
+	dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL);
+	/* Get Consistent memory for Rx DMA */
+	dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev,
+				PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
+}
+
 static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 {
 	int ret;
@@ -944,6 +1299,8 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data;
 
+	dev_dbg(&plat_dev->dev, "%s:debug\n", __func__);
+
 	master = spi_alloc_master(&board_dat->pdev->dev,
 				  sizeof(struct pch_spi_data));
 	if (!master) {
@@ -957,9 +1314,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 
 	platform_set_drvdata(plat_dev, data);
 
-	/* baseaddress + 0x20(offset) */
+	/* baseaddress + address offset) */
+	data->io_base_addr = pci_resource_start(board_dat->pdev, 1) +
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0) +
-						   0x20 * plat_dev->id;
+					 PCH_ADDRESS_SIZE * plat_dev->id;
 	if (!data->io_remap_addr) {
 		dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__);
 		ret = -ENOMEM;
@@ -980,6 +1339,7 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 	data->n_curnt_chip = 255;
 	data->status = STATUS_RUNNING;
 	data->ch = plat_dev->id;
+	data->use_dma = use_dma;
 
 	INIT_LIST_HEAD(&data->queue);
 	spin_lock_init(&data->lock);
@@ -1010,6 +1370,11 @@ static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
 		goto err_spi_register_master;
 	}
 
+	if (use_dma) {
+		dev_info(&plat_dev->dev, "Use DMA for data transfers\n");
+		pch_alloc_dma_buf(board_dat, data);
+	}
+
 	return 0;
 
 err_spi_register_master:
@@ -1029,22 +1394,27 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
 	struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
 	struct pch_spi_data *data = platform_get_drvdata(plat_dev);
 	int count;
+	unsigned long flags;
 
 	dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n",
 		__func__, plat_dev->id, board_dat->pdev->irq);
+
+	if (use_dma)
+		pch_free_dma_buf(board_dat, data);
+
 	/* check for any pending messages; no action is taken if the queue
 	 * is still full; but at least we tried.  Unload anyway */
 	count = 500;
-	spin_lock(&data->lock);
+	spin_lock_irqsave(&data->lock, flags);
 	data->status = STATUS_EXITING;
 	while ((list_empty(&data->queue) == 0) && --count) {
 		dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
 			__func__);
-		spin_unlock(&data->lock);
+		spin_unlock_irqrestore(&data->lock, flags);
 		msleep(PCH_SLEEP_TIME);
-		spin_lock(&data->lock);
+		spin_lock_irqsave(&data->lock, flags);
 	}
-	spin_unlock(&data->lock);
+	spin_unlock_irqrestore(&data->lock, flags);
 
 	pch_spi_free_resources(board_dat, data);
 	/* disable interrupts & free IRQ */
@@ -1081,8 +1451,8 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
 	/* check if the current message is processed:
 	   Only after thats done the transfer will be suspended */
 	count = 255;
-	while ((--count) > 0)
-		if (!(data->bcurrent_msg_processing)) {
+	while ((--count) > 0) {
+		if (!(data->bcurrent_msg_processing))
 			break;
 		msleep(PCH_SLEEP_TIME);
 	}
@@ -1123,7 +1493,6 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
 				"%s request_irq failed\n", __func__);
 			return retval;
 		}
-
 		/* reset PCH SPI h/w */
 		pch_spi_reset(data->master);
 		pch_spi_set_master_mode(data->master);
@@ -1213,7 +1582,6 @@ static int __devinit pch_spi_probe(struct pci_dev *pdev,
 	}
 
 	pci_set_drvdata(pdev, pd_dev_save);
-
 	return 0;
 
 err_platform_device:
@@ -1326,5 +1694,9 @@ static void __exit pch_spi_exit(void)
 }
 module_exit(pch_spi_exit);
 
+module_param(use_dma, int, 0644);
+MODULE_PARM_DESC(use_dma,
+		 "to use DMA for data transfers pass 1 else 0; default 1");
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI Driver");
-- 
1.7.4


------------------------------------------------------------------------------
EditLive Enterprise is the world's most technically advanced content
authoring tool. Experience the power of Track Changes, Inline Image
Editing and ensure content is compliant with Accessibility Checking.
http://p.sf.net/sfu/ephox-dev2dev

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-06-09  1:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-07  5:50 [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
2011-06-07  5:50 ` [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Tomoya MORINAGA
2011-06-08 22:52   ` Grant Likely
2011-06-08  8:35 ` [PATCH/RESEND v4 1/2] spi_topcliff_pch: support new device ML7213 IOH Tomoya MORINAGA
2011-06-08 14:44   ` Grant Likely
2011-06-09  1:04     ` Tomoya MORINAGA
2011-06-08 22:33 ` Grant Likely
  -- strict thread matches above, loose matches on Subject: below --
2009-01-01 23:29 [PATCH/RESEND 2/2] spi_topcliff_pch: DMA support Unknown, y
2009-01-01 23:29 y

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).