|
|
|
@ -104,9 +104,7 @@ struct spi_imx_data {
|
|
|
|
|
unsigned int dma_is_inited;
|
|
|
|
|
unsigned int dma_finished;
|
|
|
|
|
bool usedma;
|
|
|
|
|
u32 rx_wml;
|
|
|
|
|
u32 tx_wml;
|
|
|
|
|
u32 rxt_wml;
|
|
|
|
|
u32 wml;
|
|
|
|
|
struct completion dma_rx_completion;
|
|
|
|
|
struct completion dma_tx_completion;
|
|
|
|
|
|
|
|
|
@ -124,9 +122,14 @@ static inline int is_imx35_cspi(struct spi_imx_data *d)
|
|
|
|
|
return d->devtype_data->devtype == IMX35_CSPI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int is_imx51_ecspi(struct spi_imx_data *d)
|
|
|
|
|
{
|
|
|
|
|
return d->devtype_data->devtype == IMX51_ECSPI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
|
|
|
|
|
{
|
|
|
|
|
return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8;
|
|
|
|
|
return is_imx51_ecspi(d) ? 64 : 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MXC_SPI_BUF_RX(type) \
|
|
|
|
@ -201,9 +204,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
|
|
|
|
{
|
|
|
|
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
|
|
|
|
|
|
|
|
|
if (spi_imx->dma_is_inited
|
|
|
|
|
&& transfer->len > spi_imx->rx_wml * sizeof(u32)
|
|
|
|
|
&& transfer->len > spi_imx->tx_wml * sizeof(u32))
|
|
|
|
|
if (spi_imx->dma_is_inited &&
|
|
|
|
|
transfer->len > spi_imx->wml * sizeof(u32))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
@ -244,6 +246,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
|
|
|
|
#define MX51_ECSPI_STAT 0x18
|
|
|
|
|
#define MX51_ECSPI_STAT_RR (1 << 3)
|
|
|
|
|
|
|
|
|
|
#define MX51_ECSPI_TESTREG 0x20
|
|
|
|
|
#define MX51_ECSPI_TESTREG_LBC BIT(31)
|
|
|
|
|
|
|
|
|
|
/* MX51 eCSPI */
|
|
|
|
|
static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi,
|
|
|
|
|
unsigned int *fres)
|
|
|
|
@ -313,7 +318,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
|
|
|
|
{
|
|
|
|
|
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
|
|
|
|
|
u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
|
|
|
|
|
u32 clk = config->speed_hz, delay;
|
|
|
|
|
u32 clk = config->speed_hz, delay, reg;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The hardware seems to have a race condition when changing modes. The
|
|
|
|
@ -351,7 +356,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
|
|
|
|
else
|
|
|
|
|
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs);
|
|
|
|
|
|
|
|
|
|
/* CTRL register always go first to bring out controller from reset */
|
|
|
|
|
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
|
|
|
|
|
|
|
|
|
reg = readl(spi_imx->base + MX51_ECSPI_TESTREG);
|
|
|
|
|
if (config->mode & SPI_LOOP)
|
|
|
|
|
reg |= MX51_ECSPI_TESTREG_LBC;
|
|
|
|
|
else
|
|
|
|
|
reg &= ~MX51_ECSPI_TESTREG_LBC;
|
|
|
|
|
writel(reg, spi_imx->base + MX51_ECSPI_TESTREG);
|
|
|
|
|
|
|
|
|
|
writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -378,10 +392,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
|
|
|
|
if (spi_imx->dma_is_inited) {
|
|
|
|
|
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
|
|
|
|
|
|
|
|
|
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
|
|
|
|
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
|
|
|
|
rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
|
|
|
|
|
rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
|
|
|
|
tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
|
|
|
|
rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
|
|
|
|
|
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
|
|
|
|
|
& ~MX51_ECSPI_DMA_RX_WML_MASK
|
|
|
|
|
& ~MX51_ECSPI_DMA_RXT_WML_MASK)
|
|
|
|
@ -832,18 +845,21 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
|
|
|
|
if (of_machine_is_compatible("fsl,imx6dl"))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
|
|
|
|
|
/* Prepare for TX DMA: */
|
|
|
|
|
master->dma_tx = dma_request_slave_channel(dev, "tx");
|
|
|
|
|
if (!master->dma_tx) {
|
|
|
|
|
dev_err(dev, "cannot get the TX DMA channel!\n");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
|
|
|
|
|
if (IS_ERR(master->dma_tx)) {
|
|
|
|
|
ret = PTR_ERR(master->dma_tx);
|
|
|
|
|
dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret);
|
|
|
|
|
master->dma_tx = NULL;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slave_config.direction = DMA_MEM_TO_DEV;
|
|
|
|
|
slave_config.dst_addr = res->start + MXC_CSPITXDATA;
|
|
|
|
|
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
|
|
|
|
slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
slave_config.dst_maxburst = spi_imx->wml;
|
|
|
|
|
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
|
|
|
|
|
if (ret) {
|
|
|
|
|
dev_err(dev, "error in TX dma configuration.\n");
|
|
|
|
@ -851,17 +867,18 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prepare for RX : */
|
|
|
|
|
master->dma_rx = dma_request_slave_channel(dev, "rx");
|
|
|
|
|
if (!master->dma_rx) {
|
|
|
|
|
dev_dbg(dev, "cannot get the DMA channel.\n");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
master->dma_rx = dma_request_slave_channel_reason(dev, "rx");
|
|
|
|
|
if (IS_ERR(master->dma_rx)) {
|
|
|
|
|
ret = PTR_ERR(master->dma_rx);
|
|
|
|
|
dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret);
|
|
|
|
|
master->dma_rx = NULL;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slave_config.direction = DMA_DEV_TO_MEM;
|
|
|
|
|
slave_config.src_addr = res->start + MXC_CSPIRXDATA;
|
|
|
|
|
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
|
|
|
|
slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
slave_config.src_maxburst = spi_imx->wml;
|
|
|
|
|
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
|
|
|
|
|
if (ret) {
|
|
|
|
|
dev_err(dev, "error in RX dma configuration.\n");
|
|
|
|
@ -874,8 +891,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
|
|
|
|
master->max_dma_len = MAX_SDMA_BD_BYTES;
|
|
|
|
|
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
|
|
|
|
|
SPI_MASTER_MUST_TX;
|
|
|
|
|
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
|
|
|
|
spi_imx->dma_is_inited = 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -942,14 +957,22 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|
|
|
|
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
|
|
|
|
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
|
|
|
|
|
/* Change RX_DMA_LENGTH trigger dma fetch tail data */
|
|
|
|
|
left = transfer->len % spi_imx->rxt_wml;
|
|
|
|
|
left = transfer->len % spi_imx->wml;
|
|
|
|
|
if (left)
|
|
|
|
|
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
|
|
|
|
|
spi_imx->base + MX51_ECSPI_DMA);
|
|
|
|
|
/*
|
|
|
|
|
* Set these order to avoid potential RX overflow. The overflow may
|
|
|
|
|
* happen if we enable SPI HW before starting RX DMA due to rescheduling
|
|
|
|
|
* for another task and/or interrupt.
|
|
|
|
|
* So RX DMA enabled first to make sure data would be read out from FIFO
|
|
|
|
|
* ASAP. TX DMA enabled next to start filling TX FIFO with new data.
|
|
|
|
|
* And finaly SPI HW enabled to start actual data transfer.
|
|
|
|
|
*/
|
|
|
|
|
dma_async_issue_pending(master->dma_rx);
|
|
|
|
|
dma_async_issue_pending(master->dma_tx);
|
|
|
|
|
spi_imx->devtype_data->trigger(spi_imx);
|
|
|
|
|
|
|
|
|
|
dma_async_issue_pending(master->dma_tx);
|
|
|
|
|
dma_async_issue_pending(master->dma_rx);
|
|
|
|
|
/* Wait SDMA to finish the data transfer.*/
|
|
|
|
|
timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
|
|
|
|
|
IMX_DMA_TIMEOUT);
|
|
|
|
@ -958,6 +981,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|
|
|
|
dev_driver_string(&master->dev),
|
|
|
|
|
dev_name(&master->dev));
|
|
|
|
|
dmaengine_terminate_all(master->dma_tx);
|
|
|
|
|
dmaengine_terminate_all(master->dma_rx);
|
|
|
|
|
} else {
|
|
|
|
|
timeout = wait_for_completion_timeout(
|
|
|
|
|
&spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT);
|
|
|
|
@ -968,8 +992,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|
|
|
|
spi_imx->devtype_data->reset(spi_imx);
|
|
|
|
|
dmaengine_terminate_all(master->dma_rx);
|
|
|
|
|
}
|
|
|
|
|
dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK;
|
|
|
|
|
writel(dma |
|
|
|
|
|
spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
|
|
|
|
|
spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
|
|
|
|
|
spi_imx->base + MX51_ECSPI_DMA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1117,6 +1142,9 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|
|
|
|
spi_imx = spi_master_get_devdata(master);
|
|
|
|
|
spi_imx->bitbang.master = master;
|
|
|
|
|
|
|
|
|
|
spi_imx->devtype_data = of_id ? of_id->data :
|
|
|
|
|
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < master->num_chipselect; i++) {
|
|
|
|
|
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
|
|
|
|
if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
|
|
|
|
@ -1142,12 +1170,11 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|
|
|
|
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
|
|
|
|
|
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
|
|
|
|
|
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
|
|
|
|
if (is_imx51_ecspi(spi_imx))
|
|
|
|
|
spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
|
|
|
|
|
|
|
|
|
|
init_completion(&spi_imx->xfer_done);
|
|
|
|
|
|
|
|
|
|
spi_imx->devtype_data = of_id ? of_id->data :
|
|
|
|
|
(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
|
|
|
|
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
|
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
|
if (IS_ERR(spi_imx->base)) {
|
|
|
|
@ -1193,9 +1220,15 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|
|
|
|
* Only validated on i.mx6 now, can remove the constrain if validated on
|
|
|
|
|
* other chips.
|
|
|
|
|
*/
|
|
|
|
|
if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
|
|
|
|
|
&& spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
|
|
|
|
|
dev_err(&pdev->dev, "dma setup error,use pio instead\n");
|
|
|
|
|
if (is_imx51_ecspi(spi_imx)) {
|
|
|
|
|
ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res);
|
|
|
|
|
if (ret == -EPROBE_DEFER)
|
|
|
|
|
goto out_clk_put;
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
dev_err(&pdev->dev, "dma setup error %d, use pio\n",
|
|
|
|
|
ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spi_imx->devtype_data->reset(spi_imx);
|
|
|
|
|
|
|
|
|
|