linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] SPI: SSP SPI Controller driver v3
@ 2012-12-18  8:11 chao bi
  2012-12-19  9:56 ` Mika Westerberg
  0 siblings, 1 reply; 6+ messages in thread
From: chao bi @ 2012-12-18  8:11 UTC (permalink / raw)
  To: grant.likely
  Cc: linux-kernel, sylvain.centelles, ken.k.mills, jun.d.chen, alan,
	Linus Walleij


This patch is to implement SSP SPI controller driver, which has been applied and
validated on intel Moorestown & Medfield platform. The patch are originated by
Ken Mills <ken.k.mills@intel.com> and Sylvain Centelles <sylvain.centelles@intel.com>,
migrating to lateset Linux mainline SPI framework by Channing <chao.bi@intel.com>
and Chen Jun <jun.d.chen@intel.com> according to their integration & validation
on Medfield platform.

Signed-off-by: Ken Mills <ken.k.mills@intel.com>
Signed-off-by: Sylvain Centelles <sylvain.centelles@intel.com>
Signed-off-by: channing <chao.bi@intel.com>
Signed-off-by: Chen Jun <jun.d.chen@intel.com>
---
 drivers/spi/Kconfig                   |    9 +
 drivers/spi/Makefile                  |    1 +
 drivers/spi/spi-intel-mid-ssp.c       | 1614 +++++++++++++++++++++++++++++++++
 include/linux/spi/spi-intel-mid-ssp.h |  103 +++
 4 files changed, 1727 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi-intel-mid-ssp.c
 create mode 100644 include/linux/spi/spi-intel-mid-ssp.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2e188e1..6285f17 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -186,6 +186,15 @@ config SPI_IMX
 	  This enables using the Freescale i.MX SPI controllers in master
 	  mode.
 
+config SPI_INTEL_MID_SSP
+	tristate "SSP SPI controller driver for Intel MID platforms"
+	depends on SPI_MASTER && INTEL_MID_DMAC
+	help
+	  This is the unified SSP SPI master controller driver for
+	  the Intel MID platforms, handling Moorestown & Medfield,
+	  master clock mode.
+	  It supports Bulverde SSP core.
+
 config SPI_LM70_LLP
 	tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
 	depends on PARPORT && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 64e970b..1738966 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
+obj-$(CONFIG_SPI_INTEL_MID_SSP)		+= spi-intel-mid-ssp.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
diff --git a/drivers/spi/spi-intel-mid-ssp.c b/drivers/spi/spi-intel-mid-ssp.c
new file mode 100644
index 0000000..440c4a2
--- /dev/null
+++ b/drivers/spi/spi-intel-mid-ssp.c
@@ -0,0 +1,1614 @@
+/*
+ * spi-intel-mid-ssp.c
+ * This driver supports Bulverde SSP core used on Intel MID platforms
+ * It supports SSP of Moorestown & Medfield platforms and handles clock
+ * slave & master modes.
+ *
+ * Copyright (c) 2010, Intel Corporation.
+ *  Ken Mills <ken.k.mills@intel.com>
+ *  Sylvain Centelles <sylvain.centelles@intel.com>
+ *  Jun Chen <jun.d.chen@intel.com>
+ *  Chao Bi <chao.bi@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * Note:
+ *
+ * Supports DMA and non-interrupt polled transfers.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/intel_mid_dma.h>
+#include <linux/pm_qos.h>
+#include <linux/module.h>
+#include <linux/spi/spi-intel-mid-ssp.h>
+#include <linux/bitops.h>
+
+#define PCI_MRST_DMAC1_ID	0x0814
+#define PCI_MDFL_DMAC1_ID	0x0827
+
+#define SSP_NOT_SYNC BIT(22)
+#define MAX_SPI_TRANSFER_SIZE 8192
+#define MAX_BITBANGING_LOOP   10000
+#define SPI_FIFO_SIZE 16
+
+/* PM QoS define(usec) */
+#define MIN_EXIT_LATENCY 20
+
+/* SPI DMA max transfer time */
+#define SSP_SPI_DMA_TIMEOUT 100
+
+/* SSP assignement configuration from PCI config */
+#define SSP_CFG_GET_MODE(ssp_cfg)	((ssp_cfg) & 0x07)
+#define SSP_CFG_GET_SPI_BUS_NB(ssp_cfg)	(((ssp_cfg) >> 3) & 0x07)
+#define SSP_CFG_IS_SPI_SLAVE(ssp_cfg)	((ssp_cfg) & BIT(6))
+#define SSP_CFG_SPI_MODE_ID		1
+/* adid field offset is 6 inside the vendor specific capability */
+#define VNDR_CAPABILITY_ADID_OFFSET	6
+
+/* Driver's quirk flags
+ * This workarround bufferizes data in the audio fabric SDRAM from
+ * where the DMA transfers will operate. Should be enabled only for
+ * SPI slave mode. */
+#define QUIRKS_SRAM_ADDITIONAL_CPY	1
+/* If set the trailing bytes won't be handled by the DMA.
+ * Trailing byte feature not fully available. */
+#define QUIRKS_DMA_USE_NO_TRAIL		2
+/* If set, the driver will use PM_QOS to reduce the latency
+ * introduced by the deeper C-states which may produce over/under
+ * run issues. Must be used in slave mode. In master mode, the
+ * latency is not critical, but setting this workarround  may
+ * improve the SPI throughput. */
+#define QUIRKS_USE_PM_QOS		4
+/* This quirks is set on Moorestown */
+#define QUIRKS_PLATFORM_MRST		8
+/* This quirks is set on Medfield */
+#define QUIRKS_PLATFORM_MDFL		16
+/* If set, the driver will apply the bitbanging workarround needed
+ * to enable defective Langwell stepping A SSP. The defective SSP
+ * can be enabled only once, and should never be disabled. */
+#define QUIRKS_BIT_BANGING		32
+/* Address of I2C_Serbus register */
+#define I2C_ADDRESS		0xff12b000
+
+/* If set, SPI is in slave clock mode */
+#define QUIRKS_SPI_SLAVE_CLOCK_MODE	64
+
+/* Uncomment to get RX and TX short dumps after each transfer */
+#define MAX_TRAILING_BYTE_RETRY 16
+#define MAX_TRAILING_BYTE_LOOP 100
+#define DELAY_TO_GET_A_WORD 3
+#define DFLT_TIMEOUT_VAL 500
+
+#define DEFINE_SSP_REG(reg, off) \
+static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \
+static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }
+
+#define RX_DIRECTION 0
+#define TX_DIRECTION 1
+
+#define I2C_ACCESS_USDELAY 10
+
+#define DFLT_BITS_PER_WORD 16
+#define MIN_BITS_PER_WORD     4
+#define MAX_BITS_PER_WORD     32
+#define DFLT_FIFO_BURST_SIZE	IMSS_FIFO_BURST_8
+
+#define TRUNCATE(x, a) ((x) & ~((a)-1))
+
+DEFINE_SSP_REG(SSCR0, 0x00)
+DEFINE_SSP_REG(SSCR1, 0x04)
+DEFINE_SSP_REG(SSSR, 0x08)
+DEFINE_SSP_REG(SSITR, 0x0c)
+DEFINE_SSP_REG(SSDR, 0x10)
+DEFINE_SSP_REG(SSTO, 0x28)
+DEFINE_SSP_REG(SSPSP, 0x2c)
+
+DEFINE_SSP_REG(I2CCTRL, 0x00);
+DEFINE_SSP_REG(I2CDATA, 0x04);
+
+DEFINE_SSP_REG(GPLR1, 0x04);
+DEFINE_SSP_REG(GPDR1, 0x0c);
+DEFINE_SSP_REG(GPSR1, 0x14);
+DEFINE_SSP_REG(GPCR1, 0x1C);
+DEFINE_SSP_REG(GAFR1_U, 0x44);
+
+#define SYSCFG  0x20bc0
+
+#define SRAM_BASE_ADDR 0xfffdc000
+#define SRAM_RX_ADDR   SRAM_BASE_ADDR
+#define SRAM_TX_ADDR  (SRAM_BASE_ADDR + MAX_SPI_TRANSFER_SIZE)
+
+#define SSCR0_DSS   (0x0000000f)     /* Data Size Select (mask) */
+#define SSCR0_DataSize(x)  ((x) - 1)    /* Data Size Select [4..16] */
+#define SSCR0_FRF   (0x00000030)     /* FRame Format (mask) */
+#define SSCR0_Motorola        0x0         /* Motorola's SPI mode */
+#define SSCR0_ECS   BIT(6) /* External clock select */
+#define SSCR0_SSE   BIT(7) /* Synchronous Serial Port Enable */
+
+#define SSCR0_SCR   (0x000fff00)      /* Serial Clock Rate (mask) */
+#define SSCR0_SerClkDiv(x) (((x) - 1) << 8) /* Divisor [1..4096] */
+#define SSCR0_EDSS  BIT(20)           /* Extended data size select */
+#define SSCR0_NCS   BIT(21)           /* Network clock select */
+#define SSCR0_RIM    BIT(22)           /* Receive FIFO overrrun int mask */
+#define SSCR0_TUM   BIT(23)           /* Transmit FIFO underrun int mask */
+#define SSCR0_FRDC (0x07000000)     /* Frame rate divider control (mask) */
+#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame */
+#define SSCR0_ADC   BIT(30)           /* Audio clock select */
+#define SSCR0_MOD  BIT(31)           /* Mode (normal or network) */
+
+#define SSCR1_RIE    BIT(0) /* Receive FIFO Interrupt Enable */
+#define SSCR1_TIE     BIT(1) /* Transmit FIFO Interrupt Enable */
+#define SSCR1_LBM   BIT(2) /* Loop-Back Mode */
+#define SSCR1_SPO   BIT(3) /* SSPSCLK polarity setting */
+#define SSCR1_SPH   BIT(4) /* Motorola SPI SSPSCLK phase setting */
+#define SSCR1_MWDS           BIT(5) /* Microwire Transmit Data Size */
+#define SSCR1_TFT    (0x000003c0)     /* Transmit FIFO Threshold (mask) */
+#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */
+#define SSCR1_RFT    (0x00003c00)     /* Receive FIFO Threshold (mask) */
+#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
+
+#define SSSR_TNF		BIT(2)	/* Tx FIFO Not Full */
+#define SSSR_RNE		BIT(3)	/* Rx FIFO Not Empty */
+#define SSSR_BSY		BIT(4)	/* SSP Busy */
+#define SSSR_TFS		BIT(5)	/* Tx FIFO Service Request */
+#define SSSR_RFS		BIT(6)	/* Rx FIFO Service Request */
+#define SSSR_ROR		BIT(7)	/* Rx FIFO Overrun */
+#define SSSR_TFL_MASK           (0x0F << 8)     /* Tx FIFO level field mask */
+
+#define SSCR0_TIM    BIT(23)          /* Transmit FIFO Under Run Int Mask */
+#define SSCR0_RIM    BIT(22)          /* Receive FIFO Over Run int Mask */
+#define SSCR0_NCS    BIT(21)          /* Network Clock Select */
+#define SSCR0_EDSS   BIT(20)          /* Extended Data Size Select */
+
+#define SSCR0_TISSP      BIT(4)  /* TI Sync Serial Protocol */
+#define SSCR0_PSP        (3 << 4)  /* PSP - Programmable Serial Protocol */
+#define SSCR1_TTELP      BIT(31) /* TXD Tristate Enable Last Phase */
+#define SSCR1_TTE        BIT(30) /* TXD Tristate Enable */
+#define SSCR1_EBCEI      BIT(29) /* Enable Bit Count Error interrupt */
+#define SSCR1_SCFR       BIT(28) /* Slave Clock free Running */
+#define SSCR1_ECRA       BIT(27) /* Enable Clock Request A */
+#define SSCR1_ECRB       BIT(26) /* Enable Clock request B */
+#define SSCR1_SCLKDIR    BIT(25) /* Serial Bit Rate Clock Direction */
+#define SSCR1_SFRMDIR    BIT(24) /* Frame Direction */
+#define SSCR1_RWOT       BIT(23) /* Receive Without Transmit */
+#define SSCR1_TRAIL      BIT(22) /* Trailing Byte */
+#define SSCR1_TSRE       BIT(21) /* Transmit Service Request Enable */
+#define SSCR1_RSRE       BIT(20) /* Receive Service Request Enable */
+#define SSCR1_TINTE      BIT(19) /* Receiver Time-out Interrupt enable */
+#define SSCR1_PINTE      BIT(18) /* Trailing Byte Interupt Enable */
+#define SSCR1_STRF       BIT(15) /* Select FIFO or EFWR */
+#define SSCR1_EFWR       BIT(14) /* Enable FIFO Write/Read */
+#define SSCR1_IFS        BIT(16) /* Invert Frame Signal */
+
+#define SSSR_BCE         BIT(23) /* Bit Count Error */
+#define SSSR_CSS         BIT(22) /* Clock Synchronisation Status */
+#define SSSR_TUR         BIT(21) /* Transmit FIFO Under Run */
+#define SSSR_EOC         BIT(20) /* End Of Chain */
+#define SSSR_TINT        BIT(19) /* Receiver Time-out Interrupt */
+#define SSSR_PINT        BIT(18) /* Peripheral Trailing Byte Interrupt */
+
+#define SSPSP_FSRT       BIT(25)   /* Frame Sync Relative Timing */
+#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */
+#define SSPSP_SFRMWDTH(x)((x) << 16) /* Serial Frame Width */
+#define SSPSP_SFRMDLY(x) ((x) << 9)  /* Serial Frame Delay */
+#define SSPSP_DMYSTRT(x) ((x) << 7)  /* Dummy Start */
+#define SSPSP_STRTDLY(x) ((x) << 4)  /* Start Delay */
+#define SSPSP_ETDS       BIT(3)    /* End of Transfer data State */
+#define SSPSP_SFRMP      BIT(2)    /* Serial Frame Polarity */
+#define SSPSP_SCMODE(x)  ((x) << 0)  /* Serial Bit Rate Clock Mode */
+
+/*
+ * For testing SSCR1 changes that require SSP restart, basically
+ * everything except the service and interrupt enables
+ */
+
+#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_SCFR \
+				| SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \
+				| SSCR1_SFRMDIR | SSCR1_RWOT | SSCR1_TRAIL \
+				| SSCR1_IFS | SSCR1_STRF | SSCR1_EFWR \
+				| SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \
+				| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
+
+struct midssp_chip_data {
+	u32 cr0;
+	u32 cr1;
+	u32 timeout;
+	u8 n_bytes;
+	bool dma_enabled;
+	u8 bits_per_word;
+	u32 speed_hz;
+	bool (*write)(struct ssp_driver_context *drv_context);
+	bool (*read)(struct ssp_driver_context *drv_context);
+};
+
+enum intel_mid_ssp_spi_fifo_burst {
+	IMSS_FIFO_BURST_1,
+	IMSS_FIFO_BURST_4,
+	IMSS_FIFO_BURST_8
+};
+
+/* spi_board_info.controller_data for SPI slave devices,
+ * copied to spi_device.platform_data ... mostly for dma tuning
+ */
+struct intel_mid_ssp_spi_chip {
+	enum intel_mid_ssp_spi_fifo_burst burst_size;
+	u32 timeout;
+	bool enable_loopback;
+	bool dma_enabled;
+};
+
+MODULE_AUTHOR("Ken Mills");
+MODULE_DESCRIPTION("Bulverde SSP core SPI contoller");
+MODULE_LICENSE("GPL");
+
+static inline bool is_tx_fifo_empty(struct ssp_driver_context *drv_context)
+{
+	u32 sssr;
+	sssr = read_SSSR(drv_context->ioaddr);
+	return ((sssr & SSSR_TNF) && ((sssr & SSSR_TFL_MASK) == 0));
+}
+
+static inline bool is_rx_fifo_empty(struct ssp_driver_context *drv_context)
+{
+	return ((read_SSSR(drv_context->ioaddr) & SSSR_RNE) == 0);
+}
+
+static inline void disable_interface(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
+}
+
+static inline void disable_triggers(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	write_SSCR1(read_SSCR1(reg) & ~drv_context->cr1_sig, reg);
+}
+
+
+static void flush(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	u32 i = 0;
+
+	/* If the transmit fifo is not empty, reset the interface. */
+	if (!is_tx_fifo_empty(drv_context)) {
+		dev_err(&drv_context->pdev->dev,
+				"TX FIFO not empty. Reset of SPI IF");
+		disable_interface(drv_context);
+		return;
+	}
+
+	dev_dbg(&drv_context->pdev->dev, " SSSR=%x\r\n", read_SSSR(reg));
+	while (!is_rx_fifo_empty(drv_context) && (i < SPI_FIFO_SIZE + 1)) {
+		read_SSDR(reg);
+		i++;
+	}
+	WARN(i > 0, "%d words flush occured\n", i);
+
+	return;
+}
+
+static bool null_writer(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	u8 n_bytes = drv_context->n_bytes;
+
+	if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
+		|| (drv_context->tx == drv_context->tx_end))
+		return false;
+
+	write_SSDR(0, reg);
+	drv_context->tx += n_bytes;
+
+	return true;
+}
+
+static bool null_reader(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	u8 n_bytes = drv_context->n_bytes;
+
+	while ((read_SSSR(reg) & SSSR_RNE)
+		&& (drv_context->rx < drv_context->rx_end)) {
+		read_SSDR(reg);
+		drv_context->rx += n_bytes;
+	}
+
+	return drv_context->rx == drv_context->rx_end;
+}
+
+static bool u8_writer(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
+		|| (drv_context->tx == drv_context->tx_end))
+		return false;
+
+	write_SSDR(*(u8 *)(drv_context->tx), reg);
+	++drv_context->tx;
+
+	return true;
+}
+
+static bool u8_reader(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	while ((read_SSSR(reg) & SSSR_RNE)
+		&& (drv_context->rx < drv_context->rx_end)) {
+		*(u8 *)(drv_context->rx) = read_SSDR(reg);
+		++drv_context->rx;
+	}
+
+	return drv_context->rx == drv_context->rx_end;
+}
+
+static bool u16_writer(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
+		|| (drv_context->tx == drv_context->tx_end))
+		return false;
+
+	write_SSDR(*(u16 *)(drv_context->tx), reg);
+	drv_context->tx += 2;
+
+	return true;
+}
+
+static bool u16_reader(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	while ((read_SSSR(reg) & SSSR_RNE)
+		&& (drv_context->rx < drv_context->rx_end)) {
+		*(u16 *)(drv_context->rx) = read_SSDR(reg);
+		drv_context->rx += 2;
+	}
+
+	return drv_context->rx == drv_context->rx_end;
+}
+
+static bool u32_writer(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
+		|| (drv_context->tx == drv_context->tx_end))
+		return false;
+
+	write_SSDR(*(u32 *)(drv_context->tx), reg);
+	drv_context->tx += 4;
+
+	return true;
+}
+
+static bool u32_reader(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	while ((read_SSSR(reg) & SSSR_RNE)
+		&& (drv_context->rx < drv_context->rx_end)) {
+		*(u32 *)(drv_context->rx) = read_SSDR(reg);
+		drv_context->rx += 4;
+	}
+
+	return drv_context->rx == drv_context->rx_end;
+}
+
+static bool chan_filter(struct dma_chan *chan, void *param)
+{
+	struct ssp_driver_context *drv_context =
+		(struct ssp_driver_context *)param;
+	bool ret = false;
+
+	if (!drv_context->dmac1)
+		return ret;
+
+	if (chan->device->dev == &drv_context->dmac1->dev)
+		ret = true;
+
+	return ret;
+}
+
+/**
+ * unmap_dma_buffers() - Unmap the DMA buffers used during the last transfer.
+ * @drv_context:	Pointer to the private driver context
+ */
+static void unmap_dma_buffers(struct ssp_driver_context *drv_context)
+{
+	struct device *dev = &drv_context->pdev->dev;
+
+	if (!drv_context->dma_mapped)
+		return;
+	dma_unmap_single(dev, drv_context->rx_dma, drv_context->len,
+		PCI_DMA_FROMDEVICE);
+	dma_unmap_single(dev, drv_context->tx_dma, drv_context->len,
+		PCI_DMA_TODEVICE);
+	drv_context->dma_mapped = 0;
+}
+
+/**
+ * drain_trail() - Handle trailing bytes of a transfer
+ * @drv_context:	Pointer to the private driver context
+ *
+ * This function handles the trailing bytes of a transfer for the case
+ * they are not handled by the DMA.
+ */
+void drain_trail(struct ssp_driver_context *drv_context)
+{
+	struct device *dev = &drv_context->pdev->dev;
+	void *reg = drv_context->ioaddr;
+
+	if (drv_context->len != drv_context->len_dma_rx) {
+		dev_dbg(dev, "Handling trailing bytes. SSSR:%08x\n",
+			read_SSSR(reg));
+		drv_context->rx += drv_context->len_dma_rx;
+		drv_context->tx += drv_context->len_dma_tx;
+
+		while ((drv_context->tx != drv_context->tx_end) ||
+			(drv_context->rx != drv_context->rx_end)) {
+			drv_context->read(drv_context);
+			drv_context->write(drv_context);
+		}
+	}
+}
+
+/**
+ * sram_to_ddr_cpy() - Copy data from Langwell SDRAM to DDR
+ * @drv_context:	Pointer to the private driver context
+ */
+static void sram_to_ddr_cpy(struct ssp_driver_context *drv_context)
+{
+	u32 length = drv_context->len;
+
+	if ((drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL)
+		&& (drv_context->len > drv_context->rx_fifo_threshold *
+		drv_context->n_bytes))
+		length = TRUNCATE(drv_context->len,
+			drv_context->rx_fifo_threshold * drv_context->n_bytes);
+
+	memcpy_fromio(drv_context->rx, drv_context->virt_addr_sram_rx, length);
+}
+
+/**
+ * int_transfer_complete() - Handle completion of current message
+ * @drv_context:	Pointer to the private driver context
+ */
+static void int_transfer_complete(struct ssp_driver_context *drv_context)
+{
+	void *reg = drv_context->ioaddr;
+	struct device *dev = &drv_context->pdev->dev;
+
+	if (unlikely(drv_context->quirks & QUIRKS_USE_PM_QOS))
+		pm_qos_update_request(&drv_context->pm_qos_req,
+					PM_QOS_DEFAULT_VALUE);
+
+	if (unlikely(drv_context->quirks & QUIRKS_SRAM_ADDITIONAL_CPY))
+		sram_to_ddr_cpy(drv_context);
+
+	if (likely(drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL))
+		drain_trail(drv_context);
+	else
+		/* Stop getting Time Outs */
+		write_SSTO(0, reg);
+
+	drv_context->cur_msg->status = 0;
+	drv_context->cur_msg->actual_length = drv_context->len;
+
+	dev_dbg(dev, "End of transfer. SSSR:%08X\n", read_SSSR(reg));
+
+	/*Current message is completed, notify the core to handle it*/
+	spi_finalize_current_message(drv_context->master);
+}
+
+/**
+ * intel_mid_ssp_spi_dma_done() - End of DMA transfer callback
+ * @arg:	Pointer to the data provided at callback registration
+ *
+ * This function is set as callback for both RX and TX DMA transfers. The
+ * RX or TX 'done' flag is set acording to the direction of the ended
+ * transfer. Then, if both RX and TX flags are set, it means that the
+ * transfer job is completed.
+ */
+static void intel_mid_ssp_spi_dma_done(void *arg)
+{
+	struct callback_param *cb_param = (struct callback_param *)arg;
+	struct ssp_driver_context *drv_context = cb_param->drv_context;
+	struct device *dev = &drv_context->pdev->dev;
+	void *reg = drv_context->ioaddr;
+
+	if (cb_param->direction == TX_DIRECTION)
+		drv_context->txdma_done = 1;
+	else
+		drv_context->rxdma_done = 1;
+
+	dev_dbg(dev, "DMA callback for direction %d [RX done:%d] [TX done:%d]\n",
+		cb_param->direction, drv_context->rxdma_done,
+		drv_context->txdma_done);
+
+	if (drv_context->txdma_done && drv_context->rxdma_done) {
+		/* Clear Status Register */
+		write_SSSR(drv_context->clear_sr, reg);
+		dev_dbg(dev, "DMA done\n");
+		/* Disable Triggers to DMA or to CPU */
+		disable_triggers(drv_context);
+		unmap_dma_buffers(drv_context);
+		/* DMA done callback */
+		complete(&drv_context->dma_done);
+	}
+}
+
+/**
+ * intel_mid_ssp_spi_dma_init() - Initialize DMA
+ * @drv_context:	Pointer to the private driver context
+ *
+ * This function is called at driver setup phase to allocate DMA
+ * ressources.
+ */
+static void intel_mid_ssp_spi_dma_init(struct ssp_driver_context *drv_context)
+{
+	struct intel_mid_dma_slave *rxs, *txs;
+	struct dma_slave_config *ds;
+	dma_cap_mask_t mask;
+	struct device *dev = &drv_context->pdev->dev;
+	unsigned int device_id;
+
+	/* Configure RX channel parameters */
+	rxs = &drv_context->dmas_rx;
+	ds = &rxs->dma_slave;
+
+	ds->direction = DMA_FROM_DEVICE;
+	rxs->hs_mode = LNW_DMA_HW_HS;
+	rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
+	ds->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	ds->src_addr_width = drv_context->n_bytes;
+
+	/* Use a DMA burst according to the FIFO thresholds */
+	if (drv_context->rx_fifo_threshold == 8) {
+		ds->src_maxburst = 8;
+		ds->dst_maxburst = 8;
+	} else if (drv_context->rx_fifo_threshold == 4) {
+		ds->src_maxburst = 4;
+		ds->dst_maxburst = 4;
+	} else {
+		ds->src_maxburst = 1;
+		ds->dst_maxburst = 1;
+	}
+
+	/* Configure TX channel parameters */
+	txs = &drv_context->dmas_tx;
+	ds = &txs->dma_slave;
+
+	ds->direction = DMA_TO_DEVICE;
+	txs->hs_mode = LNW_DMA_HW_HS;
+	txs->cfg_mode = LNW_DMA_MEM_TO_PER;
+	ds->src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	ds->dst_addr_width = drv_context->n_bytes;
+
+	/* Use a DMA burst according to the FIFO thresholds */
+	if (drv_context->rx_fifo_threshold == 8) {
+		ds->src_maxburst = 8;
+		ds->dst_maxburst = 8;
+	} else if (drv_context->rx_fifo_threshold == 4) {
+		ds->src_maxburst = 4;
+		ds->dst_maxburst = 4;
+	} else {
+		ds->src_maxburst = 1;
+		ds->dst_maxburst = 1;
+	}
+
+	/* Nothing more to do if already initialized */
+	if (drv_context->dma_initialized)
+		return;
+
+	/* Use DMAC1 */
+	if (drv_context->quirks & QUIRKS_PLATFORM_MRST)
+		device_id = PCI_MRST_DMAC1_ID;
+	else
+		device_id = PCI_MDFL_DMAC1_ID;
+
+	drv_context->dmac1 = pci_get_device(PCI_VENDOR_ID_INTEL,
+							device_id, NULL);
+
+	if (!drv_context->dmac1) {
+		dev_err(dev, "Can't find DMAC1");
+		return;
+	}
+
+	if (drv_context->quirks & QUIRKS_SRAM_ADDITIONAL_CPY) {
+		drv_context->virt_addr_sram_rx = devm_ioremap_nocache(dev,
+				SRAM_BASE_ADDR, 2 * MAX_SPI_TRANSFER_SIZE);
+		if (drv_context->virt_addr_sram_rx)
+			drv_context->virt_addr_sram_tx =
+				drv_context->virt_addr_sram_rx +
+				MAX_SPI_TRANSFER_SIZE;
+		else
+			dev_err(dev, "Virt_addr_sram_rx is null\n");
+	}
+
+	/* 1. Allocate rx channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	drv_context->rxchan = dma_request_channel(mask, chan_filter,
+		drv_context);
+	if (!drv_context->rxchan)
+		goto err_exit;
+
+	drv_context->rxchan->private = rxs;
+
+	/* 2. Allocate tx channel */
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+
+	drv_context->txchan = dma_request_channel(mask, chan_filter,
+		drv_context);
+
+	if (!drv_context->txchan)
+		goto free_rxchan;
+	else
+		drv_context->txchan->private = txs;
+
+	/* set the dma done bit to 1 */
+	drv_context->txdma_done = 1;
+	drv_context->rxdma_done = 1;
+
+	drv_context->tx_param.drv_context  = drv_context;
+	drv_context->tx_param.direction = TX_DIRECTION;
+	drv_context->rx_param.drv_context  = drv_context;
+	drv_context->rx_param.direction = RX_DIRECTION;
+
+	drv_context->dma_initialized = 1;
+
+	return;
+
+free_rxchan:
+	dma_release_channel(drv_context->rxchan);
+err_exit:
+	dev_err(dev, "Error : DMA Channel Not available\n");
+
+	if (drv_context->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)
+		devm_iounmap(dev, drv_context->virt_addr_sram_rx);
+
+	pci_dev_put(drv_context->dmac1);
+	return;
+}
+
+/**
+ * intel_mid_ssp_spi_dma_exit() - Release DMA ressources
+ * @drv_context:	Pointer to the private driver context
+ */
+static void intel_mid_ssp_spi_dma_exit(struct ssp_driver_context *drv_context)
+{
+	dma_release_channel(drv_context->txchan);
+	dma_release_channel(drv_context->rxchan);
+
+	if (drv_context->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)
+		devm_iounmap(&drv_context->pdev->dev,
+			drv_context->virt_addr_sram_rx);
+
+	pci_dev_put(drv_context->dmac1);
+}
+
+/**
+ * dma_transfer() - Initiate a DMA transfer
+ * @drv_context:	Pointer to the private driver context
+ */
+static void dma_transfer(struct ssp_driver_context *drv_context)
+{
+	dma_addr_t ssdr_addr;
+	struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
+	struct dma_chan *txchan, *rxchan;
+	enum dma_ctrl_flags flag;
+	struct device *dev = &drv_context->pdev->dev;
+
+	/* get Data Read/Write address */
+	ssdr_addr = (dma_addr_t)(drv_context->paddr + 0x10);
+
+	if (drv_context->tx_dma)
+		drv_context->txdma_done = 0;
+
+	if (drv_context->rx_dma)
+		drv_context->rxdma_done = 0;
+
+	drv_context->dma_done.done = 0;
+
+	/* 2. prepare the RX dma transfer */
+	txchan = drv_context->txchan;
+	rxchan = drv_context->rxchan;
+
+	flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+
+	if (likely(drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL)) {
+		/* Since the DMA is configured to do 32bits access
+		 * to/from the DDR, the DMA transfer size must be
+		 * a multiple of 4 bytes */
+		drv_context->len_dma_rx = drv_context->len & ~(4 - 1);
+		drv_context->len_dma_tx = drv_context->len_dma_rx;
+
+		/* In Rx direction, TRAIL Bytes are handled by memcpy */
+		if (drv_context->rx_dma &&
+			(drv_context->len_dma_rx >
+			drv_context->rx_fifo_threshold * drv_context->n_bytes))
+			drv_context->len_dma_rx =
+					TRUNCATE(drv_context->len_dma_rx,
+					drv_context->rx_fifo_threshold *
+					drv_context->n_bytes);
+		else if (!drv_context->rx_dma)
+			dev_err(dev, "ERROR : rx_dma is null\r\n");
+	} else {
+		/* TRAIL Bytes are handled by DMA */
+		if (drv_context->rx_dma) {
+			drv_context->len_dma_rx = drv_context->len;
+			drv_context->len_dma_tx = drv_context->len;
+		} else {
+			dev_err(dev, "ERROR : drv_context->rx_dma is null!\n");
+		}
+	}
+
+	rxdesc = rxchan->device->device_prep_dma_memcpy
+		(rxchan,				/* DMA Channel */
+		drv_context->rx_dma,			/* DAR */
+		ssdr_addr,				/* SAR */
+		drv_context->len_dma_rx,		/* Data Length */
+		flag);					/* Flag */
+
+	if (rxdesc) {
+		rxdesc->callback = intel_mid_ssp_spi_dma_done;
+		rxdesc->callback_param = &drv_context->rx_param;
+	} else {
+		dev_dbg(dev, "rxdesc is null! (len_dma_rx:%zd)\n",
+			drv_context->len_dma_rx);
+		drv_context->rxdma_done = 1;
+	}
+
+	/* 3. prepare the TX dma transfer */
+	if (drv_context->tx_dma) {
+		txdesc = txchan->device->device_prep_dma_memcpy
+		(txchan,				/* DMA Channel */
+		ssdr_addr,				/* DAR */
+		drv_context->tx_dma,			/* SAR */
+		drv_context->len_dma_tx,		/* Data Length */
+		flag);					/* Flag */
+		if (txdesc) {
+			txdesc->callback = intel_mid_ssp_spi_dma_done;
+			txdesc->callback_param = &drv_context->tx_param;
+		} else {
+			dev_dbg(dev, "txdesc is null! (len_dma_tx:%zd)\n",
+				drv_context->len_dma_tx);
+			drv_context->txdma_done = 1;
+		}
+	} else {
+		dev_err(dev, "ERROR : drv_context->tx_dma is null!\n");
+		return;
+	}
+
+	dev_info(dev, "DMA transfer len:%zd len_dma_tx:%zd len_dma_rx:%zd\n",
+		drv_context->len, drv_context->len_dma_tx,
+		drv_context->len_dma_rx);
+
+	if (rxdesc || txdesc) {
+		if (rxdesc) {
+			dev_dbg(dev, "Firing DMA RX channel\n");
+			rxdesc->tx_submit(rxdesc);
+		}
+		if (txdesc) {
+			dev_dbg(dev, "Firing DMA TX channel\n");
+			txdesc->tx_submit(txdesc);
+		}
+	} else {
+		struct callback_param cb_param;
+		cb_param.drv_context = drv_context;
+		dev_dbg(dev, "Bypassing DMA transfer\n");
+		intel_mid_ssp_spi_dma_done(&cb_param);
+	}
+}
+
+/**
+ * map_dma_buffers() - Map DMA buffer before a transfer
+ * @drv_context:	Pointer to the private drivzer context
+ */
+static int map_dma_buffers(struct ssp_driver_context *drv_context)
+{
+	struct device *dev = &drv_context->pdev->dev;
+
+	if (unlikely(drv_context->dma_mapped)) {
+		dev_err(dev, "ERROR : DMA buffers already mapped\n");
+		return 0;
+	}
+	if (unlikely(drv_context->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)) {
+		/* Copy drv_context->tx into sram_tx */
+		memcpy_toio(drv_context->virt_addr_sram_tx, drv_context->tx,
+			drv_context->len);
+		drv_context->rx_dma = SRAM_RX_ADDR;
+		drv_context->tx_dma = SRAM_TX_ADDR;
+	} else {
+		/* no QUIRKS_SRAM_ADDITIONAL_CPY */
+		if (unlikely(drv_context->dma_mapped))
+			return 1;
+
+		drv_context->tx_dma =
+			dma_map_single(dev, drv_context->tx, drv_context->len,
+				PCI_DMA_TODEVICE);
+		if (unlikely(dma_mapping_error(dev, drv_context->tx_dma))) {
+			dev_err(dev, "ERROR : tx dma mapping failed\n");
+			return 0;
+		}
+
+		drv_context->rx_dma =
+			dma_map_single(dev, drv_context->rx, drv_context->len,
+				PCI_DMA_FROMDEVICE);
+		if (unlikely(dma_mapping_error(dev, drv_context->rx_dma))) {
+			dma_unmap_single(dev, drv_context->tx_dma,
+				drv_context->len, DMA_TO_DEVICE);
+			dev_err(dev, "ERROR : rx dma mapping failed\n");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static void poll_transfer_complete(struct ssp_driver_context *drv_context)
+{
+	struct spi_message *msg;
+
+	/* Update total byte transfered return count actual bytes read */
+	drv_context->cur_msg->actual_length +=
+		drv_context->len - (drv_context->rx_end - drv_context->rx);
+
+	drv_context->cur_msg->status = 0;
+
+	msg = drv_context->cur_msg;
+	if (likely(msg->complete))
+		msg->complete(msg->context);
+}
+
+/**
+ * ssp_int() - Interrupt handler
+ * @irq
+ * @dev_id
+ *
+ * The SSP interrupt is not used for transfer which are handled by
+ * DMA or polling: only under/over run are catched to detect
+ * broken transfers.
+ */
+static irqreturn_t ssp_int(int irq, void *dev_id)
+{
+	struct ssp_driver_context *drv_context = dev_id;
+	void *reg = drv_context->ioaddr;
+	struct device *dev = &drv_context->pdev->dev;
+	u32 status = read_SSSR(reg);
+
+	/**
+	 * It should never be our interrupt since SSP will
+	 * only trigs interrupt for under/over run.
+	 */
+	if (likely(!(status & drv_context->mask_sr)))
+		return IRQ_NONE;
+
+	if (status & SSSR_ROR || status & SSSR_TUR) {
+		dev_err(dev, "--- SPI ROR or TUR occurred : SSSR=%x\n",	status);
+		WARN_ON(1);
+		if (status & SSSR_ROR)
+			dev_err(dev, "we have Overrun\n");
+		if (status & SSSR_TUR)
+			dev_err(dev, "we have Underrun\n");
+	}
+
+	/* We can fall here when not using DMA mode */
+	if (!drv_context->cur_msg) {
+		disable_interface(drv_context);
+		disable_triggers(drv_context);
+	}
+	/* clear status register */
+	write_SSSR(drv_context->clear_sr, reg);
+	return IRQ_HANDLED;
+}
+
+static void poll_transfer(struct ssp_driver_context *drv_context)
+{
+	if (drv_context->tx)
+		while (drv_context->tx != drv_context->tx_end) {
+			drv_context->write(drv_context);
+			drv_context->read(drv_context);
+		}
+
+	while (!drv_context->read(drv_context))
+		cpu_relax();
+
+	poll_transfer_complete(drv_context);
+}
+
+/**
+ * start_bitbanging() - Clock synchronization by bit banging
+ * @drv_context:	Pointer to private driver context
+ *
+ * This clock synchronization will be removed as soon as it is
+ * handled by the SCU.
+ */
+static void start_bitbanging(struct ssp_driver_context *drv_context)
+{
+	u32 sssr;
+	u32 count = 0;
+	u32 cr0;
+	void *i2c_reg = drv_context->i2c_ioaddr;
+	struct device *dev = &drv_context->pdev->dev;
+	void *reg = drv_context->ioaddr;
+	struct midssp_chip_data *chip = \
+		spi_get_ctldata(drv_context->cur_msg->spi);
+	cr0 = chip->cr0;
+
+	dev_warn(dev, "In %s : Starting bit banging\n",\
+		__func__);
+	if (read_SSSR(reg) & SSP_NOT_SYNC)
+		dev_warn(dev, "SSP clock desynchronized.\n");
+	if (!(read_SSCR0(reg) & SSCR0_SSE))
+		dev_warn(dev, "in SSCR0, SSP disabled.\n");
+
+	dev_dbg(dev, "SSP not ready, start CLK sync\n");
+
+	write_SSCR0(cr0 & ~SSCR0_SSE, reg);
+	write_SSPSP(0x02010007, reg);
+
+	write_SSTO(chip->timeout, reg);
+	write_SSCR0(cr0, reg);
+
+	/* This routine uses the DFx block to override the SSP inputs
+	 * and outputs allowing us to bit bang SSPSCLK. On Langwell,
+	 * we have to generate the clock to clear busy.
+	 */
+	write_I2CDATA(0x3, i2c_reg);
+	udelay(I2C_ACCESS_USDELAY);
+	write_I2CCTRL(0x01070034, i2c_reg);
+	udelay(I2C_ACCESS_USDELAY);
+	write_I2CDATA(0x00000099, i2c_reg);
+	udelay(I2C_ACCESS_USDELAY);
+	write_I2CCTRL(0x01070038, i2c_reg);
+	udelay(I2C_ACCESS_USDELAY);
+	sssr = read_SSSR(reg);
+
+	/* Bit bang the clock until CSS clears */
+	while ((sssr & 0x400000) && (count < MAX_BITBANGING_LOOP)) {
+		write_I2CDATA(0x2, i2c_reg);
+		udelay(I2C_ACCESS_USDELAY);
+		write_I2CCTRL(0x01070034, i2c_reg);
+		udelay(I2C_ACCESS_USDELAY);
+		write_I2CDATA(0x3, i2c_reg);
+		udelay(I2C_ACCESS_USDELAY);
+		write_I2CCTRL(0x01070034, i2c_reg);
+		udelay(I2C_ACCESS_USDELAY);
+		sssr = read_SSSR(reg);
+		count++;
+	}
+	if (count >= MAX_BITBANGING_LOOP)
+		dev_err(dev,
+			"ERROR in %s : infinite loop on bit banging. Aborting\n",
+			__func__);
+
+	dev_dbg(dev, "---Bit bang count=%d\n", count);
+
+	write_I2CDATA(0x0, i2c_reg);
+	udelay(I2C_ACCESS_USDELAY);
+	write_I2CCTRL(0x01070038, i2c_reg);
+}
+
+static unsigned int ssp_get_clk_div(int speed)
+{
+	return max(100000000 / speed, 4) - 1;
+}
+
+/**
+ * intel_mid_ssp_spi_prepare_transfer() - Prepare for a SPI transfer
+ * @master:	Pointer to the spi_master struct
+ */
+static int intel_mid_ssp_spi_prepare_transfer(struct spi_master *master)
+{
+	struct ssp_driver_context *drv_context = \
+		spi_master_get_devdata(master);
+
+	dev_dbg(&drv_context->pdev->dev, "prepare for spi transfer\n");
+
+	return 0;
+}
+
+/**
+ * intel_mid_ssp_spi_unprepare_transfer() - Prepare for a SPI transfer
+ * @master:	Pointer to the spi_master struct
+ */
+static int intel_mid_ssp_spi_unprepare_transfer(struct spi_master *master)
+{
+	struct ssp_driver_context *drv_context = \
+		spi_master_get_devdata(master);
+
+	dev_dbg(&drv_context->pdev->dev, "unprepare for spi transfer\n");
+
+	return 0;
+}
+
+/**
+ * intel_mid_ssp_spi_transfer_one() - Start a SPI transfer
+ * @master:	Pointer to the spi_master struct
+ * @msg:	Pointer to the spi_message struct
+ */
+static int intel_mid_ssp_spi_transfer_one(struct spi_master *master, \
+	struct spi_message *msg)
+{
+	struct ssp_driver_context *drv_context = \
+	spi_master_get_devdata(master);
+	struct midssp_chip_data *chip = NULL;
+	struct spi_transfer *transfer = NULL;
+	void *reg = drv_context->ioaddr;
+	u32 cr1, ret;
+	struct device *dev = &drv_context->pdev->dev;
+	chip = spi_get_ctldata(msg->spi);
+
+	msg->actual_length = 0;
+	msg->status = -EINPROGRESS;
+	drv_context->cur_msg = msg;
+
+	/* We handle only one transfer message since the protocol module has to
+	   control the out of band signaling. */
+	transfer = list_entry(msg->transfers.next,
+					struct spi_transfer,
+					transfer_list);
+
+	/* Check transfer length */
+	if (unlikely((transfer->len > MAX_SPI_TRANSFER_SIZE) ||
+		(transfer->len == 0))) {
+		dev_warn(dev, "transfer length null or greater than %d\n",
+			MAX_SPI_TRANSFER_SIZE);
+		dev_warn(dev, "length = %d\n", transfer->len);
+		msg->status = -EINVAL;
+
+		if (msg->complete)
+			msg->complete(msg->context);
+
+		return 0;
+	}
+
+	/* Flush any remaining data (in case of failed previous transfer) */
+	flush(drv_context);
+
+	drv_context->tx  = (void *)transfer->tx_buf;
+	drv_context->rx  = (void *)transfer->rx_buf;
+	drv_context->len = transfer->len;
+	drv_context->write = chip->write;
+	drv_context->read = chip->read;
+
+	if (likely(chip->dma_enabled)) {
+		drv_context->dma_mapped = map_dma_buffers(drv_context);
+		if (unlikely(!drv_context->dma_mapped))
+			return 0;
+	} else {
+		drv_context->write = drv_context->tx ?
+			chip->write : null_writer;
+		drv_context->read  = drv_context->rx ?
+			chip->read : null_reader;
+	}
+	drv_context->tx_end = drv_context->tx + transfer->len;
+	drv_context->rx_end = drv_context->rx + transfer->len;
+
+	/* Clear status  */
+	write_SSSR(drv_context->clear_sr, reg);
+
+	/* setup the CR1 control register */
+	cr1 = chip->cr1 | drv_context->cr1_sig;
+
+	if (likely(drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL)) {
+		/* in case of len smaller than burst size, adjust the RX
+		 * threshold. All other cases will use the default threshold
+		 * value. The RX fifo threshold must be aligned with the DMA
+		 * RX transfer size, which may be limited to a multiple of 4
+		 * bytes due to 32bits DDR access.
+		 */
+		if  (drv_context->len / drv_context->n_bytes <=
+			drv_context->rx_fifo_threshold) {
+			u32 rx_fifo_threshold;
+
+			rx_fifo_threshold = (drv_context->len & ~(4 - 1)) /
+				drv_context->n_bytes;
+			cr1 &= ~(SSCR1_RFT);
+			cr1 |= SSCR1_RxTresh(rx_fifo_threshold)
+					& SSCR1_RFT;
+		} else {
+			write_SSTO(chip->timeout, reg);
+		}
+	}
+
+	dev_dbg(dev,
+		"transfer len:%zd  n_bytes:%d  cr0:%x  cr1:%x",
+		drv_context->len, drv_context->n_bytes, chip->cr0, cr1);
+
+	/* first set CR1 */
+	write_SSCR1(cr1, reg);
+
+	/* Do bitbanging only if SSP not-enabled or not-synchronized */
+	if (unlikely(((read_SSSR(reg) & SSP_NOT_SYNC) ||
+		(!(read_SSCR0(reg) & SSCR0_SSE))) &&
+		(drv_context->quirks & QUIRKS_BIT_BANGING))) {
+			start_bitbanging(drv_context);
+	} else {
+		/* (re)start the SSP */
+		write_SSCR0(chip->cr0, reg);
+	}
+
+	if (likely(chip->dma_enabled)) {
+		if (unlikely(drv_context->quirks & QUIRKS_USE_PM_QOS))
+			pm_qos_update_request(&drv_context->pm_qos_req,
+				MIN_EXIT_LATENCY);
+		dma_transfer(drv_context);
+		ret = wait_for_completion_timeout(&drv_context->dma_done,
+				msecs_to_jiffies(SSP_SPI_DMA_TIMEOUT));
+		if (!ret) {
+			dev_err(dev, "DMA transfer timeout\n");
+			ret = -ETIMEDOUT;
+			return ret;
+		}
+
+	} else {
+		poll_transfer(drv_context);
+	}
+
+	/* Handle completed message*/
+	int_transfer_complete(drv_context);
+
+	return 0;
+}
+
+/**
+ * setup() - Driver setup procedure
+ * @spi:	Pointeur to the spi_device struct
+ */
+static int setup(struct spi_device *spi)
+{
+	struct intel_mid_ssp_spi_chip *chip_info = NULL;
+	struct midssp_chip_data *chip;
+	struct ssp_driver_context *drv_context =
+		spi_master_get_devdata(spi->master);
+	u32 tx_fifo_threshold;
+	u32 burst_size;
+	u32 clk_div;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = DFLT_BITS_PER_WORD;
+
+	if ((spi->bits_per_word < MIN_BITS_PER_WORD
+		|| spi->bits_per_word > MAX_BITS_PER_WORD))
+		return -EINVAL;
+
+	chip = spi_get_ctldata(spi);
+	if (!chip) {
+		chip = kzalloc(sizeof(struct midssp_chip_data), GFP_KERNEL);
+		if (!chip) {
+			dev_err(&spi->dev,
+			"failed setup: can't allocate chip data\n");
+			return -ENOMEM;
+		}
+	}
+	chip->cr0 = SSCR0_Motorola | SSCR0_DataSize(spi->bits_per_word > 16 ?
+		spi->bits_per_word - 16 : spi->bits_per_word)
+			| SSCR0_SSE
+			| (spi->bits_per_word > 16 ? SSCR0_EDSS : 0);
+
+	/* protocol drivers may change the chip settings, so...
+	 * if chip_info exists, use it
+	 */
+	chip_info = spi->controller_data;
+
+	/* chip_info isn't always needed */
+	chip->cr1 = 0;
+	if (chip_info) {
+		burst_size = chip_info->burst_size;
+		if (burst_size > IMSS_FIFO_BURST_8)
+			burst_size = DFLT_FIFO_BURST_SIZE;
+
+		chip->timeout = chip_info->timeout;
+
+		if (chip_info->enable_loopback)
+			chip->cr1 |= SSCR1_LBM;
+
+		chip->dma_enabled = chip_info->dma_enabled;
+
+	} else {
+		/* if no chip_info provided by protocol driver,
+		 * set default values
+		 */
+		dev_info(&spi->dev, "setting default chip values\n");
+
+		burst_size = DFLT_FIFO_BURST_SIZE;
+
+		chip->dma_enabled = true;
+		if (drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL)
+			chip->timeout = 0;
+		else
+			chip->timeout = DFLT_TIMEOUT_VAL;
+	}
+	/* Set FIFO thresholds according to burst_size */
+	if (burst_size == IMSS_FIFO_BURST_8)
+		drv_context->rx_fifo_threshold = 8;
+	else if (burst_size == IMSS_FIFO_BURST_4)
+		drv_context->rx_fifo_threshold = 4;
+	else
+		drv_context->rx_fifo_threshold = 1;
+	tx_fifo_threshold = SPI_FIFO_SIZE - drv_context->rx_fifo_threshold;
+	chip->cr1 |= (SSCR1_RxTresh(drv_context->rx_fifo_threshold) &
+		SSCR1_RFT) | (SSCR1_TxTresh(tx_fifo_threshold) &
+		SSCR1_TFT);
+
+	drv_context->dma_mapped = 0;
+
+	/* setting phase and polarity. spi->mode comes from boardinfo */
+	if ((spi->mode & SPI_CPHA) != 0)
+		chip->cr1 |= SSCR1_SPH;
+	if ((spi->mode & SPI_CPOL) != 0)
+		chip->cr1 |= SSCR1_SPO;
+
+	if (drv_context->quirks & QUIRKS_SPI_SLAVE_CLOCK_MODE)
+		/* set slave mode */
+		chip->cr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+	chip->cr1 |= SSCR1_SCFR;        /* clock is not free running */
+
+	dev_dbg(&spi->dev, "%d bits/word, mode %d\n",
+		spi->bits_per_word,
+		spi->mode & 0x3);
+	if (spi->bits_per_word <= 8) {
+		chip->n_bytes = 1;
+		chip->read = u8_reader;
+		chip->write = u8_writer;
+	} else if (spi->bits_per_word <= 16) {
+		chip->n_bytes = 2;
+		chip->read = u16_reader;
+		chip->write = u16_writer;
+	} else if (spi->bits_per_word <= 32) {
+		chip->cr0 |= SSCR0_EDSS;
+		chip->n_bytes = 4;
+		chip->read = u32_reader;
+		chip->write = u32_writer;
+	} else {
+		dev_err(&spi->dev, "invalid wordsize\n");
+		return -EINVAL;
+	}
+
+	if ((drv_context->quirks & QUIRKS_SPI_SLAVE_CLOCK_MODE) == 0) {
+		chip->speed_hz = spi->max_speed_hz;
+		clk_div = ssp_get_clk_div(chip->speed_hz);
+		chip->cr0 |= clk_div << 8;
+	}
+	chip->bits_per_word = spi->bits_per_word;
+
+	spi_set_ctldata(spi, chip);
+
+	/* setup of drv_context members that will not change across transfers */
+	drv_context->n_bytes = chip->n_bytes;
+
+	if (chip->dma_enabled) {
+		intel_mid_ssp_spi_dma_init(drv_context);
+		drv_context->cr1_sig  = SSCR1_TSRE | SSCR1_RSRE;
+		drv_context->mask_sr  = SSSR_ROR | SSSR_TUR;
+		if (drv_context->quirks & QUIRKS_DMA_USE_NO_TRAIL)
+			drv_context->cr1_sig  |= SSCR1_TRAIL;
+	} else {
+		drv_context->cr1_sig = SSCR1_RIE | SSCR1_TIE | SSCR1_TINTE;
+		drv_context->mask_sr = SSSR_RFS | SSSR_TFS |
+				 SSSR_ROR | SSSR_TUR | SSSR_TINT;
+	}
+	drv_context->clear_sr = SSSR_TUR  | SSSR_ROR | SSSR_TINT;
+
+	return 0;
+}
+
+/**
+ * cleanup() - Driver cleanup procedure
+ * @spi:	Pointer to the spi_device struct
+ */
+static void cleanup(struct spi_device *spi)
+{
+	struct midssp_chip_data *chip = spi_get_ctldata(spi);
+	struct ssp_driver_context *drv_context =
+		spi_master_get_devdata(spi->master);
+
+	if (drv_context->dma_initialized)
+		intel_mid_ssp_spi_dma_exit(drv_context);
+
+	/* Remove the PM_QOS request */
+	if (drv_context->quirks & QUIRKS_USE_PM_QOS)
+		pm_qos_remove_request(&drv_context->pm_qos_req);
+
+	kfree(chip);
+	spi_set_ctldata(spi, NULL);
+}
+
+/**
+ * intel_mid_ssp_spi_probe() - Driver probe procedure
+ * @pdev:	Pointer to the pci_dev struct
+ * @ent:	Pointer to the pci_device_id struct
+ */
+static int intel_mid_ssp_spi_probe(struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	struct device *dev = &pdev->dev;
+	struct spi_master *master;
+	struct ssp_driver_context *drv_context = 0;
+	int status;
+	u32 iolen = 0;
+	u8 ssp_cfg;
+	int pos;
+	void __iomem *syscfg_ioaddr;
+	unsigned long syscfg;
+
+	/**
+	 * Check if the SSP we are probed for has been allocated
+	 * to operate as SPI. This information is retreived from
+	 * the field adid of the Vendor-Specific PCI capability
+	 * which is used as a configuration register.
+	 */
+	pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+	if (pos > 0) {
+		pci_read_config_byte(pdev,
+			pos + VNDR_CAPABILITY_ADID_OFFSET,
+			&ssp_cfg);
+	} else {
+		dev_info(dev, "No Vendor Specific PCI capability\n");
+		goto err_abort_probe;
+	}
+
+	if (SSP_CFG_GET_MODE(ssp_cfg) != SSP_CFG_SPI_MODE_ID) {
+		dev_info(dev, "Unsupported SSP mode (%02xh)\n",
+			ssp_cfg);
+		goto err_abort_probe;
+	}
+
+	dev_info(dev, "found PCI SSP controller(ID: %04xh:%04xh cfg: %02xh)\n",
+		pdev->vendor, pdev->device, ssp_cfg);
+
+	status = pci_enable_device(pdev);
+	if (status)
+		return status;
+
+	/* Allocate Slave with space for drv_context and null dma buffer */
+	master = spi_alloc_master(dev, sizeof(struct ssp_driver_context));
+
+	if (!master) {
+		dev_err(dev, "cannot alloc spi_slave\n");
+		status = -ENOMEM;
+		goto err_free_0;
+	}
+
+	drv_context = spi_master_get_devdata(master);
+	drv_context->master = master;
+
+	drv_context->pdev = pdev;
+	drv_context->quirks = ent->driver_data;
+
+	/* Set platform & configuration quirks */
+	if (drv_context->quirks & QUIRKS_PLATFORM_MRST) {
+		/* Apply bit banging workarround on MRST */
+		drv_context->quirks |= QUIRKS_BIT_BANGING;
+		/* MRST slave mode workarrounds */
+		if (SSP_CFG_IS_SPI_SLAVE(ssp_cfg))
+			drv_context->quirks |=
+				QUIRKS_USE_PM_QOS |
+				QUIRKS_SRAM_ADDITIONAL_CPY;
+	}
+	drv_context->quirks |= QUIRKS_DMA_USE_NO_TRAIL;
+	if (SSP_CFG_IS_SPI_SLAVE(ssp_cfg))
+		drv_context->quirks |= QUIRKS_SPI_SLAVE_CLOCK_MODE;
+
+	master->mode_bits = SPI_CPOL | SPI_CPHA;
+	master->bus_num = SSP_CFG_GET_SPI_BUS_NB(ssp_cfg);
+	master->num_chipselect = 1;
+	master->cleanup = cleanup;
+	master->setup = setup;
+	master->prepare_transfer_hardware = intel_mid_ssp_spi_prepare_transfer;
+	master->unprepare_transfer_hardware = \
+		intel_mid_ssp_spi_unprepare_transfer;
+	master->transfer_one_message = intel_mid_ssp_spi_transfer_one;
+
+	drv_context->dma_initialized = 0;
+	init_completion(&drv_context->dma_done);
+
+	/* get basic io resource and map it */
+	drv_context->paddr = pci_resource_start(pdev, 0);
+	iolen = pci_resource_len(pdev, 0);
+
+	status = pci_request_region(pdev, 0, dev_name(&pdev->dev));
+	if (status)
+		goto err_free_1;
+
+	drv_context->ioaddr =
+		devm_ioremap_nocache(dev, drv_context->paddr, iolen);
+	if (!drv_context->ioaddr) {
+		status = -ENOMEM;
+		goto err_free_2;
+	}
+	dev_dbg(dev, "paddr = : %08lx", drv_context->paddr);
+	dev_dbg(dev, "ioaddr = : %p\n", drv_context->ioaddr);
+	dev_dbg(dev, "attaching to IRQ: %04x\n", pdev->irq);
+	dev_dbg(dev, "quirks = : %08lx\n", drv_context->quirks);
+
+	if (drv_context->quirks & QUIRKS_BIT_BANGING) {
+		/**
+		 * Bit banging on the clock is done through
+		 * DFT which is available through I2C.
+		 * get base address of I2C_Serbus registers
+		 */
+		drv_context->i2c_daddr = I2C_ADDRESS;
+		drv_context->i2c_ioaddr =
+			devm_ioremap_nocache(dev, drv_context->i2c_daddr, 0x10);
+		if (!drv_context->i2c_ioaddr) {
+			status = -ENOMEM;
+			goto err_free_3;
+		}
+	}
+
+	/* Attach to IRQ */
+	drv_context->irq = pdev->irq;
+	status = devm_request_irq(dev, drv_context->irq, ssp_int, IRQF_SHARED,
+		"intel_mid_ssp_spi", drv_context);
+	if (status < 0) {
+		dev_err(&pdev->dev, "can not get IRQ\n");
+		goto err_free_4;
+	}
+
+	if (drv_context->quirks & QUIRKS_PLATFORM_MDFL) {
+		/* get base address of DMA selector. */
+		syscfg = drv_context->paddr - SYSCFG;
+		syscfg_ioaddr = devm_ioremap_nocache(dev, syscfg, 0x10);
+		if (!syscfg_ioaddr) {
+			status = -ENOMEM;
+			goto err_free_5;
+		}
+		iowrite32(ioread32(syscfg_ioaddr) | 2, syscfg_ioaddr);
+	}
+
+	/* Register with the SPI framework */
+	dev_info(dev, "register with SPI framework (bus spi%d)\n",
+		master->bus_num);
+
+	status = spi_register_master(master);
+
+	if (status != 0) {
+		dev_err(dev, "problem registering spi\n");
+		goto err_free_5;
+	}
+
+	pci_set_drvdata(pdev, drv_context);
+
+	/* Create the PM_QOS request */
+	if (drv_context->quirks & QUIRKS_USE_PM_QOS)
+		pm_qos_add_request(&drv_context->pm_qos_req,
+		PM_QOS_CPU_DMA_LATENCY,
+		PM_QOS_DEFAULT_VALUE);
+
+	return status;
+
+err_free_5:
+	devm_free_irq(dev, drv_context->irq, drv_context);
+err_free_4:
+	devm_iounmap(dev, drv_context->i2c_ioaddr);
+err_free_3:
+	devm_iounmap(dev, drv_context->ioaddr);
+err_free_2:
+	pci_release_region(pdev, 0);
+err_free_1:
+	spi_master_put(master);
+err_free_0:
+	pci_disable_device(pdev);
+
+	return status;
+err_abort_probe:
+	dev_info(dev, "Abort probe for SSP %04xh:%04xh\n",
+		pdev->vendor, pdev->device);
+	return -ENODEV;
+}
+
+/**
+ * intel_mid_ssp_spi_remove() - driver remove procedure
+ * @pdev:	Pointer to the pci_dev struct
+ */
+static void __devexit intel_mid_ssp_spi_remove(struct pci_dev *pdev)
+{
+	struct ssp_driver_context *drv_context = pci_get_drvdata(pdev);
+
+	if (!drv_context)
+		return;
+
+	/* Release IRQ */
+	devm_free_irq(&pdev->dev, drv_context->irq, drv_context);
+
+	devm_iounmap(&pdev->dev, drv_context->ioaddr);
+	if (drv_context->quirks & QUIRKS_BIT_BANGING)
+		devm_iounmap(&pdev->dev, drv_context->i2c_ioaddr);
+
+	/* disconnect from the SPI framework */
+	spi_unregister_master(drv_context->master);
+
+	pci_set_drvdata(pdev, NULL);
+	pci_release_region(pdev, 0);
+	pci_disable_device(pdev);
+
+	return;
+}
+
+#ifdef CONFIG_PM
+/**
+ * intel_mid_ssp_spi_suspend() - Driver suspend procedure
+ * @pdev:	Pointer to the pci_dev struct
+ * @state:	pm_message_t
+ */
+static int intel_mid_ssp_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	return 0;
+}
+
+/**
+ * intel_mid_ssp_spi_resume() - Driver resume procedure
+ * @pdev:	Pointer to the pci_dev struct
+ */
+static int intel_mid_ssp_spi_resume(struct pci_dev *pdev)
+{
+	return 0;
+}
+#else
+#define intel_mid_ssp_spi_suspend NULL
+#define intel_mid_ssp_spi_resume NULL
+#endif /* CONFIG_PM */
+
+
+static const struct pci_device_id midssp_pci_ids[] __devinitdata = {
+	/* MRST SSP0 */
+	{ PCI_VDEVICE(INTEL, 0x0815), QUIRKS_PLATFORM_MRST},
+	/* MDFL SSP0 */
+	{ PCI_VDEVICE(INTEL, 0x0832), QUIRKS_PLATFORM_MDFL},
+	/* MDFL SSP1 */
+	{ PCI_VDEVICE(INTEL, 0x0825), QUIRKS_PLATFORM_MDFL},
+	/* MDFL SSP3 */
+	{ PCI_VDEVICE(INTEL, 0x0816), QUIRKS_PLATFORM_MDFL},
+	/* MRFL SSP5 */
+	{ PCI_VDEVICE(INTEL, 0x1194), 0},
+	{},
+};
+
+static struct pci_driver intel_mid_ssp_spi_driver = {
+	.name =		"intel_mid_ssp_spi_unified",
+	.id_table =	midssp_pci_ids,
+	.probe =	intel_mid_ssp_spi_probe,
+	.remove =	__devexit_p(intel_mid_ssp_spi_remove),
+	.suspend =	intel_mid_ssp_spi_suspend,
+	.resume =	intel_mid_ssp_spi_resume,
+};
+
+static int __init intel_mid_ssp_spi_init(void)
+{
+	return pci_register_driver(&intel_mid_ssp_spi_driver);
+}
+
+late_initcall(intel_mid_ssp_spi_init);
+
+static void __exit intel_mid_ssp_spi_exit(void)
+{
+	pci_unregister_driver(&intel_mid_ssp_spi_driver);
+}
+
+module_exit(intel_mid_ssp_spi_exit);
+
diff --git a/include/linux/spi/spi-intel-mid-ssp.h b/include/linux/spi/spi-intel-mid-ssp.h
new file mode 100644
index 0000000..522f347
--- /dev/null
+++ b/include/linux/spi/spi-intel-mid-ssp.h
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (C) Intel 2009
+ *  Ken Mills <ken.k.mills@intel.com>
+ *  Sylvain Centelles <sylvain.centelles@intel.com>
+ *  Jun Chen <jun.d.chen@intel.com>
+ *  Chao Bi <chao.bi@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+#ifndef INTEL_MID_SSP_SPI_H_
+#define INTEL_MID_SSP_SPI_H_
+#include <linux/pci.h>
+#include <linux/spi/spi.h>
+
+struct callback_param {
+	void *drv_context;
+	u32 direction;
+};
+
+struct ssp_driver_context {
+	/* Driver model hookup */
+	struct pci_dev *pdev;
+
+	/* SPI framework hookup */
+	struct spi_master *master;
+
+	/* SSP register addresses */
+	unsigned long paddr;
+	void *ioaddr;
+	int irq;
+
+	/* I2C registers */
+	dma_addr_t i2c_daddr;
+	void *i2c_ioaddr;
+
+	/* SSP masks*/
+	u32 cr1_sig;
+	u32 cr1;
+	u32 clear_sr;
+	u32 mask_sr;
+
+	/* PM_QOS request */
+	struct pm_qos_request pm_qos_req;
+
+	struct tasklet_struct poll_transfer;
+
+	spinlock_t lock;
+
+	/* Current message transfer state info */
+	struct spi_message *cur_msg;
+	size_t len;
+	size_t len_dma_rx;
+	size_t len_dma_tx;
+	void *tx;
+	void *tx_end;
+	void *rx;
+	void *rx_end;
+	bool dma_initialized;
+	int dma_mapped;
+	dma_addr_t rx_dma;
+	dma_addr_t tx_dma;
+	u8 n_bytes;
+	bool (*write)(struct ssp_driver_context *drv_context);
+	bool (*read)(struct ssp_driver_context *drv_context);
+
+	struct intel_mid_dma_slave    dmas_tx;
+	struct intel_mid_dma_slave    dmas_rx;
+	struct dma_chan    *txchan;
+	struct dma_chan    *rxchan;
+
+	u8 __iomem *virt_addr_sram_tx;
+	u8 __iomem *virt_addr_sram_rx;
+
+	int txdma_done;
+	int rxdma_done;
+	struct callback_param tx_param;
+	struct callback_param rx_param;
+	struct pci_dev *dmac1;
+
+	unsigned long quirks;
+	u32 rx_fifo_threshold;
+
+	struct completion dma_done;
+};
+
+#endif /*INTEL_MID_SSP_SPI_H_*/
-- 
1.7.1




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

* Re: [PATCH] SPI: SSP SPI Controller driver v3
  2012-12-18  8:11 [PATCH] SPI: SSP SPI Controller driver v3 chao bi
@ 2012-12-19  9:56 ` Mika Westerberg
  2013-01-06 23:36   ` Linus Walleij
  0 siblings, 1 reply; 6+ messages in thread
From: Mika Westerberg @ 2012-12-19  9:56 UTC (permalink / raw)
  To: chao bi
  Cc: grant.likely, linux-kernel, sylvain.centelles, ken.k.mills,
	jun.d.chen, alan, Linus Walleij

On Tue, Dec 18, 2012 at 04:11:36PM +0800, chao bi wrote:
> 
> This patch is to implement SSP SPI controller driver, which has been applied and
> validated on intel Moorestown & Medfield platform. The patch are originated by
> Ken Mills <ken.k.mills@intel.com> and Sylvain Centelles <sylvain.centelles@intel.com>,
> migrating to lateset Linux mainline SPI framework by Channing <chao.bi@intel.com>
> and Chen Jun <jun.d.chen@intel.com> according to their integration & validation
> on Medfield platform.

This is the same IP block as used in PXA, right? With few modifications
here and there. Is there a reason not to use spi-pxa2xx.c?

I have a set of patches for spi-pxa2xx.c that adds support for Lynxpoint
LPSS, DMA engine and ACPI enumeration. I'm hoping to send those soon (once
-rc1 is released) to the list.

Maybe we can co-operate to get the Medfield support there as well?

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

* Re: [PATCH] SPI: SSP SPI Controller driver v3
  2012-12-19  9:56 ` Mika Westerberg
@ 2013-01-06 23:36   ` Linus Walleij
  2013-01-07  8:05     ` channing
  0 siblings, 1 reply; 6+ messages in thread
From: Linus Walleij @ 2013-01-06 23:36 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: chao bi, grant.likely, linux-kernel, sylvain.centelles,
	ken.k.mills, jun.d.chen, alan

On Wed, Dec 19, 2012 at 10:56 AM, Mika Westerberg
<mika.westerberg@linux.intel.com> wrote:
> On Tue, Dec 18, 2012 at 04:11:36PM +0800, chao bi wrote:
>>
>> This patch is to implement SSP SPI controller driver, which has been applied and
>> validated on intel Moorestown & Medfield platform. The patch are originated by
>> Ken Mills <ken.k.mills@intel.com> and Sylvain Centelles <sylvain.centelles@intel.com>,
>> migrating to lateset Linux mainline SPI framework by Channing <chao.bi@intel.com>
>> and Chen Jun <jun.d.chen@intel.com> according to their integration & validation
>> on Medfield platform.
>
> This is the same IP block as used in PXA, right? With few modifications
> here and there. Is there a reason not to use spi-pxa2xx.c?

This needs to be investigated. Two drivers for the same or closely related
hardware block is never a good sign...

Yours,
Linus Walleij

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

* Re: [PATCH] SPI: SSP SPI Controller driver v3
  2013-01-06 23:36   ` Linus Walleij
@ 2013-01-07  8:05     ` channing
  2013-01-07 10:21       ` Linus Walleij
  0 siblings, 1 reply; 6+ messages in thread
From: channing @ 2013-01-07  8:05 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mika Westerberg, grant.likely, linux-kernel, sylvain.centelles,
	ken.k.mills, jun.d.chen, alan

On Mon, 2013-01-07 at 00:36 +0100, Linus Walleij wrote:
> On Wed, Dec 19, 2012 at 10:56 AM, Mika Westerberg
> <mika.westerberg@linux.intel.com> wrote:
> > On Tue, Dec 18, 2012 at 04:11:36PM +0800, chao bi wrote:
> >>
> >> This patch is to implement SSP SPI controller driver, which has been applied and
> >> validated on intel Moorestown & Medfield platform. The patch are originated by
> >> Ken Mills <ken.k.mills@intel.com> and Sylvain Centelles <sylvain.centelles@intel.com>,
> >> migrating to lateset Linux mainline SPI framework by Channing <chao.bi@intel.com>
> >> and Chen Jun <jun.d.chen@intel.com> according to their integration & validation
> >> on Medfield platform.
> >
> > This is the same IP block as used in PXA, right? With few modifications
> > here and there. Is there a reason not to use spi-pxa2xx.c?
> 
> This needs to be investigated. Two drivers for the same or closely related
> hardware block is never a good sign...
> 
> Yours,
> Linus Walleij

Dear Linus ,Mika and Grant,

Thanks for your remind.

Frankly I'm currently not sure whether they share same IP.. per your reminds, I tried to find but get 
limited info about PXA SSP's IP, from the code, looks like they have part of registers the same. 

As far as I know, spi-pxa2xx.c is specific for SSP controller of PXA2XX/PXA3XX core, right? While Medfield 
platform is embedded with ATOM core, the SSP driver we upload is validated on SSP controller of ATOM. In my 
view, they're specific for different AP & Platforms, if compare the 2 files, there are still many difference 
in how they works, if to choose a driver for Intel Medfild/Moorestown platform, I believe spi-intel-mid-ssp.c 
driver could be a more mature solution. 

What do you think? please correct me if I'm mistaken.

-chao


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

* Re: [PATCH] SPI: SSP SPI Controller driver v3
  2013-01-07  8:05     ` channing
@ 2013-01-07 10:21       ` Linus Walleij
  2013-01-07 10:36         ` Mika Westerberg
  0 siblings, 1 reply; 6+ messages in thread
From: Linus Walleij @ 2013-01-07 10:21 UTC (permalink / raw)
  To: channing
  Cc: Mika Westerberg, grant.likely, linux-kernel, sylvain.centelles,
	ken.k.mills, jun.d.chen, alan

On Mon, Jan 7, 2013 at 9:05 AM, channing <chao.bi@intel.com> wrote:

> Frankly I'm currently not sure whether they share same IP.. per your reminds, I tried to find but get
> limited info about PXA SSP's IP, from the code, looks like they have part of registers the same.
>
> As far as I know, spi-pxa2xx.c is specific for SSP controller of PXA2XX/PXA3XX core, right?

As pointed out by Mika it may very well be the same IP anyway. People copy/paste
share and fork VHDL/Verilog/SystemC code just as much as they do with
kernel code.

> While Medfield
> platform is embedded with ATOM core, the SSP driver we upload is validated on SSP controller of ATOM. In my
> view, they're specific for different AP & Platforms, if compare the 2 files, there are still many difference
> in how they works, if to choose a driver for Intel Medfild/Moorestown platform, I believe spi-intel-mid-ssp.c
> driver could be a more mature solution.

>From the kernel community point of view the driver that is being
maintained in-tree
is the more mature solution.

For your productization maybe the other driver is more mature.

These are two different definitions of maturity.

In the kernel we worry that we do not needlessly need to fix bugs in two places
or leave a bug in one place while fixing it in another place, which is
potentially the
problem we run into here.

There may be an initial time/testing cost for you to adopt to the PXA driver,
but on the other hand the PXA developers will review your code and fix
bugs for you also, so it's what we call a win-win situation if you can share
the driver.

Yours,
Linus Walleij

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

* Re: [PATCH] SPI: SSP SPI Controller driver v3
  2013-01-07 10:21       ` Linus Walleij
@ 2013-01-07 10:36         ` Mika Westerberg
  0 siblings, 0 replies; 6+ messages in thread
From: Mika Westerberg @ 2013-01-07 10:36 UTC (permalink / raw)
  To: Linus Walleij
  Cc: channing, grant.likely, linux-kernel, sylvain.centelles,
	ken.k.mills, jun.d.chen, alan

On Mon, Jan 07, 2013 at 11:21:04AM +0100, Linus Walleij wrote:
> On Mon, Jan 7, 2013 at 9:05 AM, channing <chao.bi@intel.com> wrote:
> 
> > Frankly I'm currently not sure whether they share same IP.. per your reminds, I tried to find but get
> > limited info about PXA SSP's IP, from the code, looks like they have part of registers the same.
> >
> > As far as I know, spi-pxa2xx.c is specific for SSP controller of PXA2XX/PXA3XX core, right?
> 
> As pointed out by Mika it may very well be the same IP anyway. People copy/paste
> share and fork VHDL/Verilog/SystemC code just as much as they do with
> kernel code.

Yes, at least on Intel Lynxpoint it is the same IP with few additional
features. I'm about to send patches to spi-pxa2xx which enable the
Lynxpoint support shortly.

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

end of thread, other threads:[~2013-01-07 10:33 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-12-18  8:11 [PATCH] SPI: SSP SPI Controller driver v3 chao bi
2012-12-19  9:56 ` Mika Westerberg
2013-01-06 23:36   ` Linus Walleij
2013-01-07  8:05     ` channing
2013-01-07 10:21       ` Linus Walleij
2013-01-07 10:36         ` Mika Westerberg

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).