@@ -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);
@@ -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) {
@@ -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;
@@ -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 {
@@ -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,