diff mbox

spi/pxa2xx: add support for Intel Broxton

Message ID 1429573714-68828-1-git-send-email-qipeng.zha@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

qipeng.zha April 20, 2015, 11:48 p.m. UTC
From: Huiquan Zhong <huiquan.zhong@intel.com>

Broxton have 3 SPI ports, PCI ids are 0x0ac2, 0x0ac4, 0x0ac6.
The LPSS SSP private register offset is 0x200. iDMA register offset
is 0x800.

Every Broxton SPI Controller have integrated one dedicated DMA IP.
Two DMA channels mapping:
	channel[0] -> SPI Tx,
	channel[1] -> SPI Rx.

BXT SSP SPI does not support DMA burst mode operation. Only iDMA
M-Size setting equal to 1(Single) is supported for all DMA
peripherial transfers from the SSP to the DMA and from the DMA
to the SSP, otherwise RX data corruption will happen.

Use a new parameter(.chip_select) for BXT dedicated cs control.

Signed-off-by: Huiquan Zhong <huiquan.zhong@intel.com>
---
 drivers/spi/spi-pxa2xx-pci.c   | 103 ++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-pxa2xx.c       | 105 +++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-pxa2xx.h       |   1 +
 include/linux/pxa2xx_ssp.h     |   3 ++
 include/linux/spi/pxa2xx_spi.h |   2 +
 5 files changed, 214 insertions(+)

Comments

Jarkko Nikula April 21, 2015, 1:44 p.m. UTC | #1
Hi Huiquan and Qipeng

On 04/21/2015 02:48 AM, qipeng.zha wrote:
> From: Huiquan Zhong <huiquan.zhong@intel.com>
>
> Broxton have 3 SPI ports, PCI ids are 0x0ac2, 0x0ac4, 0x0ac6.
> The LPSS SSP private register offset is 0x200. iDMA register offset
> is 0x800.
>
> Every Broxton SPI Controller have integrated one dedicated DMA IP.
> Two DMA channels mapping:
> 	channel[0] -> SPI Tx,
> 	channel[1] -> SPI Rx.
>
> BXT SSP SPI does not support DMA burst mode operation. Only iDMA
> M-Size setting equal to 1(Single) is supported for all DMA
> peripherial transfers from the SSP to the DMA and from the DMA
> to the SSP, otherwise RX data corruption will happen.
>
> Use a new parameter(.chip_select) for BXT dedicated cs control.
>
> Signed-off-by: Huiquan Zhong <huiquan.zhong@intel.com>
> ---
>   drivers/spi/spi-pxa2xx-pci.c   | 103 ++++++++++++++++++++++++++++++++++++++++
>   drivers/spi/spi-pxa2xx.c       | 105 +++++++++++++++++++++++++++++++++++++++++
>   drivers/spi/spi-pxa2xx.h       |   1 +
>   include/linux/pxa2xx_ssp.h     |   3 ++
>   include/linux/spi/pxa2xx_spi.h |   2 +
>   5 files changed, 214 insertions(+)
>
Can we hold this a bit and join our efforts? We have been cooking a 
support for Skylake recently with a plan to extend to Broxton later.

Major difference to patch here is that we plan to use common code that 
probes PCI and ACPI enumerated individual devices (I2C, SPI and UART) 
and their integrated DMA (which doesn't have its own PCI or ACPI ID and 
not all devices have it) and adds them as platform devices.

That module will take care of reset and clock management so for instance 
this SPI code becomes a bit shorter and avoids similar code in I2C and 
UART too. Our MFD loader has been posted recently but is not merged yet:

https://lkml.org/lkml/2015/3/31/258
Huiquan Zhong April 22, 2015, 2:59 a.m. UTC | #2
OK.

-----Original Message-----
From: Jarkko Nikula [mailto:jarkko.nikula@linux.intel.com] 
Sent: Tuesday, April 21, 2015 9:45 PM
To: Zha, Qipeng; linux-spi@vger.kernel.org
Cc: broonie@kernel.org; Yang, Fei; Zhong, Huiquan; Chen, Jason CJ; Zheng, Qi; Andy Shevchenko
Subject: Re: [PATCH] spi/pxa2xx: add support for Intel Broxton

Hi Huiquan and Qipeng

On 04/21/2015 02:48 AM, qipeng.zha wrote:
> From: Huiquan Zhong <huiquan.zhong@intel.com>
>
> Broxton have 3 SPI ports, PCI ids are 0x0ac2, 0x0ac4, 0x0ac6.
> The LPSS SSP private register offset is 0x200. iDMA register offset is 
> 0x800.
>
> Every Broxton SPI Controller have integrated one dedicated DMA IP.
> Two DMA channels mapping:
> 	channel[0] -> SPI Tx,
> 	channel[1] -> SPI Rx.
>
> BXT SSP SPI does not support DMA burst mode operation. Only iDMA 
> M-Size setting equal to 1(Single) is supported for all DMA peripherial 
> transfers from the SSP to the DMA and from the DMA to the SSP, 
> otherwise RX data corruption will happen.
>
> Use a new parameter(.chip_select) for BXT dedicated cs control.
>
> Signed-off-by: Huiquan Zhong <huiquan.zhong@intel.com>
> ---
>   drivers/spi/spi-pxa2xx-pci.c   | 103 ++++++++++++++++++++++++++++++++++++++++
>   drivers/spi/spi-pxa2xx.c       | 105 +++++++++++++++++++++++++++++++++++++++++
>   drivers/spi/spi-pxa2xx.h       |   1 +
>   include/linux/pxa2xx_ssp.h     |   3 ++
>   include/linux/spi/pxa2xx_spi.h |   2 +
>   5 files changed, 214 insertions(+)
>
Can we hold this a bit and join our efforts? We have been cooking a support for Skylake recently with a plan to extend to Broxton later.

Major difference to patch here is that we plan to use common code that probes PCI and ACPI enumerated individual devices (I2C, SPI and UART) and their integrated DMA (which doesn't have its own PCI or ACPI ID and not all devices have it) and adds them as platform devices.

That module will take care of reset and clock management so for instance this SPI code becomes a bit shorter and avoids similar code in I2C and UART too. Our MFD loader has been posted recently but is not merged yet:

https://lkml.org/lkml/2015/3/31/258

--
Jarkko
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index fa7399e..f042557 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -12,6 +12,7 @@ 
 
 #include <linux/dmaengine.h>
 #include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
 
 enum {
 	PORT_CE4100,
@@ -20,6 +21,9 @@  enum {
 	PORT_BSW1,
 	PORT_BSW2,
 	PORT_QUARK_X1000,
+	PORT_BXT0,
+	PORT_BXT1,
+	PORT_BXT2,
 };
 
 struct pxa_spi_info {
@@ -31,6 +35,9 @@  struct pxa_spi_info {
 	/* DMA channel request parameters */
 	void *tx_param;
 	void *rx_param;
+
+	int (*setup)(struct pci_dev *pdev, struct pxa2xx_spi_master *spi_pdata,
+			struct pxa_spi_info *c);
 };
 
 static struct dw_dma_slave byt_tx_param = { .dst_id = 0 };
@@ -54,6 +61,75 @@  static bool lpss_dma_filter(struct dma_chan *chan, void *param)
 	return true;
 }
 
+static struct dw_dma_platform_data bxt_idma_pdata = {
+	.nr_channels = 2,
+	.is_private = true,
+	.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+	.chan_priority = CHAN_PRIORITY_ASCENDING,
+	.block_size = 0x1ffff, /* 128KB -1 */
+};
+
+#define BXT_IDMA_OFFSET	0x800
+static struct dw_dma_chip *bxt_idma_probe(struct device *dev,
+		void __iomem *base, int irq)
+{
+	struct dw_dma_chip *chip;
+	int err;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return ERR_PTR(-ENOMEM);
+
+	chip->irq = irq;
+	chip->regs = base + BXT_IDMA_OFFSET;
+	chip->type = DW_DMAC_TYPE_IDMA;
+
+	err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (err)
+		return ERR_PTR(err);
+
+	chip->dev = dev;
+	err = dw_dma_probe(chip, &bxt_idma_pdata);
+	if (err)
+		return ERR_PTR(err);
+
+	return chip;
+}
+
+static int bxt_spi_setup(struct pci_dev *dev,
+		struct pxa2xx_spi_master *spi_pdata, struct pxa_spi_info *c)
+{
+	struct ssp_device *ssp = &spi_pdata->ssp;
+	struct dw_dma_slave *tx_param, *rx_param;
+
+	spi_pdata->chip = bxt_idma_probe(&dev->dev, ssp->mmio_base,
+			ssp->irq);
+	if (!spi_pdata->chip) {
+		dev_err(&dev->dev, "error: DMA device register failed\n");
+		return PTR_ERR(spi_pdata->chip);
+	}
+
+	tx_param = devm_kzalloc(&dev->dev, sizeof(*tx_param), GFP_KERNEL);
+	if (!tx_param)
+		return -ENOMEM;
+
+	rx_param = devm_kzalloc(&dev->dev, sizeof(*rx_param), GFP_KERNEL);
+	if (!rx_param)
+		return -ENOMEM;
+
+	tx_param->dst_id = 0;
+	rx_param->src_id = 1;
+
+	tx_param->dma_dev = &dev->dev;
+	rx_param->dma_dev = &dev->dev;
+
+	spi_pdata->tx_param = tx_param;
+	spi_pdata->rx_param = rx_param;
+	spi_pdata->enable_dma = true;
+
+	return 0;
+}
+
 static struct pxa_spi_info spi_info_configs[] = {
 	[PORT_CE4100] = {
 		.type = PXA25x_SSP,
@@ -99,6 +175,27 @@  static struct pxa_spi_info spi_info_configs[] = {
 		.num_chipselect = 1,
 		.max_clk_rate = 50000000,
 	},
+	[PORT_BXT0] = {
+		.type = LPSS_BXT_SSP,
+		.port_id = 0,
+		.num_chipselect = 3,
+		.max_clk_rate = 50000000,
+		.setup = bxt_spi_setup,
+	},
+	[PORT_BXT1] = {
+		.type = LPSS_BXT_SSP,
+		.port_id = 1,
+		.num_chipselect = 4,
+		.max_clk_rate = 50000000,
+		.setup = bxt_spi_setup,
+	},
+	[PORT_BXT2] = {
+		.type = LPSS_BXT_SSP,
+		.port_id = 2,
+		.num_chipselect = 3,
+		.max_clk_rate = 50000000,
+		.setup = bxt_spi_setup,
+	},
 };
 
 static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
@@ -161,6 +258,9 @@  static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
 	ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
 	ssp->type = c->type;
 
+	if (c->setup)
+		c->setup(dev, &spi_pdata, c);
+
 	snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
 	ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL,
 					CLK_IS_ROOT, c->max_clk_rate);
@@ -203,6 +303,9 @@  static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
 	{ PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 },
 	{ PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 },
 	{ PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 },
+	{ PCI_VDEVICE(INTEL, 0x0ac2), PORT_BXT0 },
+	{ PCI_VDEVICE(INTEL, 0x0ac4), PORT_BXT1 },
+	{ PCI_VDEVICE(INTEL, 0x0ac6), PORT_BXT2 },
 	{ },
 };
 MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index e3223ac..2006eea 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -64,6 +64,10 @@  MODULE_ALIAS("platform:pxa2xx-spi");
 #define LPSS_TX_LOTHRESH_DFLT	160
 #define LPSS_TX_HITHRESH_DFLT	224
 
+#define BXT_RX_THRESH_DFLT	32
+#define BXT_TX_LOTHRESH_DFLT	16
+#define BXT_TX_HITHRESH_DFLT	48
+
 /* Offset from drv_data->lpss_base */
 #define GENERAL_REG		0x08
 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
@@ -72,6 +76,18 @@  MODULE_ALIAS("platform:pxa2xx-spi");
 #define SPI_CS_CONTROL_SW_MODE	BIT(0)
 #define SPI_CS_CONTROL_CS_HIGH	BIT(1)
 
+#define SSP_RESETS		0x04
+#define BXT_SSP_RESETS_IDMA	BIT(2)
+#define BXT_SSP_RESETS_HOST	0x3
+
+#define SPI_CS_CONTROL_BXT	0x24
+#define SSP_CS_CONTROL_BXT_SEL_SFT 8
+#define SPI_CS_CONTROL_BXT_POL	(0xf << 12)
+
+#define CLOCK_GATE_BXT		0x38
+#define CLOCK_GATE_IDMA_ON	(0x3 << 2)
+#define CLOCK_GATE_IP_ON	0x3
+
 static bool is_lpss_ssp(const struct driver_data *drv_data)
 {
 	return drv_data->ssp_type == LPSS_SSP;
@@ -82,6 +98,11 @@  static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
 	return drv_data->ssp_type == QUARK_X1000_SSP;
 }
 
+static bool is_lpss_bxt_ssp(const struct driver_data *drv_data)
+{
+	return drv_data->ssp_type == LPSS_BXT_SSP;
+}
+
 static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data)
 {
 	switch (drv_data->ssp_type) {
@@ -111,6 +132,9 @@  static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data)
 	case QUARK_X1000_SSP:
 		mask = QUARK_X1000_SSSR_TFL_MASK;
 		break;
+	case LPSS_BXT_SSP:
+		mask = SSITF_TFL_BXT_MASK;
+		return (pxa2xx_spi_read(drv_data, SSITF) & mask) == mask;
 	default:
 		mask = SSSR_TFL_MASK;
 		break;
@@ -183,6 +207,31 @@  static void __lpss_ssp_write_priv(struct driver_data *drv_data,
 	writel(value, drv_data->lpss_base + offset);
 }
 
+static void lpss_bxt_ssp_setup(struct driver_data *drv_data)
+{
+	unsigned offset = 0x200;
+	u32 value;
+
+	/* Now set the LPSS base */
+	drv_data->lpss_base = drv_data->ioaddr + offset;
+
+	/* Reset LPSS SSP Controller */
+	__lpss_ssp_write_priv(drv_data, SSP_RESETS, 0x0);
+	usleep_range(10, 100);
+	__lpss_ssp_write_priv(drv_data, SSP_RESETS,
+				BXT_SSP_RESETS_IDMA | BXT_SSP_RESETS_HOST);
+	usleep_range(10, 100);
+
+	/* Force IP Clock on */
+	__lpss_ssp_write_priv(drv_data, CLOCK_GATE_BXT,
+				CLOCK_GATE_IDMA_ON | CLOCK_GATE_IP_ON);
+
+	/* Set the default Idle SPI CS polarity is High, and SW control mode */
+	value = SPI_CS_CONTROL_BXT_POL | SPI_CS_CONTROL_SW_MODE
+				| SPI_CS_CONTROL_CS_HIGH;
+	__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL_BXT, value);
+}
+
 /*
  * lpss_ssp_setup - perform LPSS SSP specific setup
  * @drv_data: pointer to the driver private data
@@ -251,6 +300,21 @@  static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
 	__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
 }
 
+static void lpss_bxt_ssp_cs_control(struct driver_data *drv_data, bool enable)
+{
+	u32 value;
+
+	value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL_BXT);
+	if (enable)
+		value &= ~SPI_CS_CONTROL_CS_HIGH;
+	else
+		value |= SPI_CS_CONTROL_CS_HIGH;
+
+	value |= drv_data->cur_chip->chip_select << SSP_CS_CONTROL_BXT_SEL_SFT;
+
+	__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL_BXT, value);
+}
+
 static void cs_assert(struct driver_data *drv_data)
 {
 	struct chip_data *chip = drv_data->cur_chip;
@@ -272,6 +336,9 @@  static void cs_assert(struct driver_data *drv_data)
 
 	if (is_lpss_ssp(drv_data))
 		lpss_ssp_cs_control(drv_data, true);
+
+	if (is_lpss_bxt_ssp(drv_data))
+		lpss_bxt_ssp_cs_control(drv_data, true);
 }
 
 static void cs_deassert(struct driver_data *drv_data)
@@ -293,6 +360,9 @@  static void cs_deassert(struct driver_data *drv_data)
 
 	if (is_lpss_ssp(drv_data))
 		lpss_ssp_cs_control(drv_data, false);
+
+	if (is_lpss_bxt_ssp(drv_data))
+		lpss_bxt_ssp_cs_control(drv_data, false);
 }
 
 int pxa2xx_spi_flush(struct driver_data *drv_data)
@@ -970,6 +1040,25 @@  static void pump_transfers(unsigned long data)
 		    != chip->lpss_tx_threshold)
 			pxa2xx_spi_write(drv_data, SSITF,
 					 chip->lpss_tx_threshold);
+	} else if (is_lpss_bxt_ssp(drv_data)) {
+		if (drv_data->dma_mapped) {
+			/*
+			 * BXT SPI does not support DMA burst mode of operation,
+			 * need to set threshold corresponding with burst size.
+			 */
+			chip->lpss_rx_threshold = SSIRF_RxThresh(2);
+			chip->lpss_tx_threshold = SSITF_TxLoThresh(2)
+						| SSITF_TxHiThresh(62);
+		}
+
+		if ((pxa2xx_spi_read(drv_data, SSIRF) & 0x3f)
+		    != chip->lpss_rx_threshold)
+			pxa2xx_spi_write(drv_data, SSIRF,
+					 chip->lpss_rx_threshold);
+		if ((pxa2xx_spi_read(drv_data, SSITF) & 0x3f3f)
+		    != chip->lpss_tx_threshold)
+			pxa2xx_spi_write(drv_data, SSITF,
+					 chip->lpss_tx_threshold);
 	}
 
 	if (is_quark_x1000_ssp(drv_data) &&
@@ -1090,6 +1179,11 @@  static int setup(struct spi_device *spi)
 		tx_hi_thres = LPSS_TX_HITHRESH_DFLT;
 		rx_thres = LPSS_RX_THRESH_DFLT;
 		break;
+	case LPSS_BXT_SSP:
+		tx_thres = BXT_TX_LOTHRESH_DFLT;
+		tx_hi_thres = BXT_TX_HITHRESH_DFLT;
+		rx_thres = BXT_RX_THRESH_DFLT;
+		break;
 	default:
 		tx_thres = TX_THRESH_DFLT;
 		tx_hi_thres = 0;
@@ -1147,6 +1241,11 @@  static int setup(struct spi_device *spi)
 		chip->enable_dma = drv_data->master_info->enable_dma;
 	}
 
+	if (is_lpss_bxt_ssp(drv_data)) {
+		chip->chip_select = spi->chip_select;
+		chip->enable_dma = drv_data->master_info->enable_dma;
+	}
+
 	chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres);
 	chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres)
 				| SSITF_TxHiThresh(tx_hi_thres);
@@ -1439,6 +1538,9 @@  static int pxa2xx_spi_probe(struct platform_device *pdev)
 	if (is_lpss_ssp(drv_data))
 		lpss_ssp_setup(drv_data);
 
+	if (is_lpss_bxt_ssp(drv_data))
+		lpss_bxt_ssp_setup(drv_data);
+
 	tasklet_init(&drv_data->pump_transfers, pump_transfers,
 		     (unsigned long)drv_data);
 
@@ -1541,6 +1643,9 @@  static int pxa2xx_spi_resume(struct device *dev)
 	if (is_lpss_ssp(drv_data))
 		lpss_ssp_setup(drv_data);
 
+	if (is_lpss_bxt_ssp(drv_data))
+		lpss_bxt_ssp_setup(drv_data);
+
 	/* Start the queue running */
 	status = spi_master_resume(drv_data->master);
 	if (status != 0) {
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index 85a58c9..c2c5127 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -105,6 +105,7 @@  struct chip_data {
 	u8 enable_dma;
 	u8 bits_per_word;
 	u32 speed_hz;
+	u8 chip_select;
 	union {
 		int gpio_cs;
 		unsigned int frm;
diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h
index dab545b..0eaaa54 100644
--- a/include/linux/pxa2xx_ssp.h
+++ b/include/linux/pxa2xx_ssp.h
@@ -185,6 +185,8 @@ 
 #define SSIRF			0x48		/* RX FIFO trigger level */
 #define SSIRF_RxThresh(x)	((x) - 1)
 
+#define SSITF_TFL_BXT_MASK	(0x3f << 16)	/* Transmit FIFO Level mask */
+
 enum pxa_ssp_type {
 	SSP_UNDEFINED = 0,
 	PXA25x_SSP,  /* pxa 210, 250, 255, 26x */
@@ -196,6 +198,7 @@  enum pxa_ssp_type {
 	CE4100_SSP,
 	LPSS_SSP,
 	QUARK_X1000_SSP,
+	LPSS_BXT_SSP,
 };
 
 struct ssp_device {
diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h
index 6d36dac..f5d03da 100644
--- a/include/linux/spi/pxa2xx_spi.h
+++ b/include/linux/spi/pxa2xx_spi.h
@@ -34,6 +34,8 @@  struct pxa2xx_spi_master {
 
 	/* For non-PXA arches */
 	struct ssp_device ssp;
+	/* For BXT DMA chip */
+	struct dw_dma_chip *chip;
 };
 
 /* spi_board_info.controller_data for SPI slave devices,