2018-10-24 08:00:38

by Clark Wang

[permalink] [raw]
Subject: [PATCH V2 1/5] spi: lpspi: Add slave mode support for imx7ulp

Add SPI slave mode support for imx7ulp, in PIO mode.

Add "spi-slave" attribute in spi node of dts file to boot.

For now, slave has to send the message which is same as the length of
message master sent.

Wire connection:
GND, SCK, MISO(to MISO of slave), MOSI(to MOSI of slave), SCS

Signed-off-by: Xiaoning Wang <[email protected]>
---
V2:
- No changes.
---
drivers/spi/spi-fsl-lpspi.c | 209 ++++++++++++++++++++++++------------
1 file changed, 139 insertions(+), 70 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 51670976faa3..86cb38d98a39 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -3,6 +3,7 @@
// Freescale i.MX7ULP LPSPI driver
//
// Copyright 2016 Freescale Semiconductor, Inc.
+// Copyright 2018 NXP

#include <linux/clk.h>
#include <linux/completion.h>
@@ -54,6 +55,7 @@
#define IER_RDIE BIT(1)
#define IER_TDIE BIT(0)
#define CFGR1_PCSCFG BIT(27)
+#define CFGR1_PINCFG (BIT(24)|BIT(25))
#define CFGR1_PCSPOL BIT(8)
#define CFGR1_NOSTALL BIT(3)
#define CFGR1_MASTER BIT(0)
@@ -79,6 +81,7 @@ struct fsl_lpspi_data {
struct device *dev;
void __iomem *base;
struct clk *clk;
+ bool is_slave;

void *rx_buf;
const void *tx_buf;
@@ -86,11 +89,14 @@ struct fsl_lpspi_data {
void (*rx)(struct fsl_lpspi_data *);

u32 remain;
+ u8 watermark;
u8 txfifosize;
u8 rxfifosize;

struct lpspi_config config;
struct completion xfer_done;
+
+ bool slave_aborted;
};

static const struct of_device_id fsl_lpspi_dt_ids[] = {
@@ -137,16 +143,18 @@ static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi,
writel(enable, fsl_lpspi->base + IMX7ULP_IER);
}

-static int lpspi_prepare_xfer_hardware(struct spi_master *master)
+static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
{
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);

return clk_prepare_enable(fsl_lpspi->clk);
}

-static int lpspi_unprepare_xfer_hardware(struct spi_master *master)
+static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
{
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);

clk_disable_unprepare(fsl_lpspi->clk);

@@ -202,22 +210,26 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi,
{
u32 temp = 0;

- temp |= fsl_lpspi->config.bpw - 1;
- temp |= fsl_lpspi->config.prescale << 27;
- temp |= (fsl_lpspi->config.mode & 0x3) << 30;
- temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
-
- /*
- * Set TCR_CONT will keep SS asserted after current transfer.
- * For the first transfer, clear TCR_CONTC to assert SS.
- * For subsequent transfer, set TCR_CONTC to keep SS asserted.
- */
- temp |= TCR_CONT;
- if (is_first_xfer)
- temp &= ~TCR_CONTC;
- else
- temp |= TCR_CONTC;
-
+ if (!fsl_lpspi->is_slave) {
+ temp |= fsl_lpspi->config.bpw - 1;
+ temp |= fsl_lpspi->config.prescale << 27;
+ temp |= (fsl_lpspi->config.mode & 0x3) << 30;
+ temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
+
+ /*
+ * Set TCR_CONT will keep SS asserted after current transfer.
+ * For the first transfer, clear TCR_CONTC to assert SS.
+ * For subsequent transfer, set TCR_CONTC to keep SS asserted.
+ */
+ temp |= TCR_CONT;
+ if (is_first_xfer)
+ temp &= ~TCR_CONTC;
+ else
+ temp |= TCR_CONTC;
+ } else {
+ temp |= fsl_lpspi->config.bpw - 1;
+ temp |= (fsl_lpspi->config.mode & 0x3) << 30;
+ }
writel(temp, fsl_lpspi->base + IMX7ULP_TCR);

dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp);
@@ -227,7 +239,7 @@ static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi)
{
u32 temp;

- temp = fsl_lpspi->txfifosize >> 1 | (fsl_lpspi->rxfifosize >> 1) << 16;
+ temp = fsl_lpspi->watermark >> 1 | (fsl_lpspi->watermark >> 1) << 16;

writel(temp, fsl_lpspi->base + IMX7ULP_FCR);

@@ -253,7 +265,8 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
if (prescale == 8 && scldiv >= 256)
return -EINVAL;

- writel(scldiv, fsl_lpspi->base + IMX7ULP_CCR);
+ writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
+ fsl_lpspi->base + IMX7ULP_CCR);

dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d\n",
perclk_rate, config.speed_hz, prescale, scldiv);
@@ -270,13 +283,18 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
writel(temp, fsl_lpspi->base + IMX7ULP_CR);
writel(0, fsl_lpspi->base + IMX7ULP_CR);

- ret = fsl_lpspi_set_bitrate(fsl_lpspi);
- if (ret)
- return ret;
+ if (!fsl_lpspi->is_slave) {
+ ret = fsl_lpspi_set_bitrate(fsl_lpspi);
+ if (ret)
+ return ret;
+ }

fsl_lpspi_set_watermark(fsl_lpspi);

- temp = CFGR1_PCSCFG | CFGR1_MASTER;
+ if (!fsl_lpspi->is_slave)
+ temp = CFGR1_MASTER;
+ else
+ temp = CFGR1_PINCFG;
if (fsl_lpspi->config.mode & SPI_CS_HIGH)
temp |= CFGR1_PCSPOL;
writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
@@ -291,7 +309,8 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
static void fsl_lpspi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(spi->master);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(spi->controller);

fsl_lpspi->config.mode = spi->mode;
fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word;
@@ -315,14 +334,51 @@ static void fsl_lpspi_setup_transfer(struct spi_device *spi,
fsl_lpspi->tx = fsl_lpspi_buf_tx_u32;
}

+ if (t->len <= fsl_lpspi->txfifosize)
+ fsl_lpspi->watermark = t->len;
+ else
+ fsl_lpspi->watermark = fsl_lpspi->txfifosize;
+
fsl_lpspi_config(fsl_lpspi);
}

-static int fsl_lpspi_transfer_one(struct spi_master *master,
+static int fsl_lpspi_slave_abort(struct spi_controller *controller)
+{
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);
+
+ fsl_lpspi->slave_aborted = true;
+ complete(&fsl_lpspi->xfer_done);
+ return 0;
+}
+
+static int fsl_lpspi_wait_for_completion(struct spi_controller *controller)
+{
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);
+
+ if (fsl_lpspi->is_slave) {
+ if (wait_for_completion_interruptible(&fsl_lpspi->xfer_done) ||
+ fsl_lpspi->slave_aborted) {
+ dev_dbg(fsl_lpspi->dev, "interrupted\n");
+ return -EINTR;
+ }
+ } else {
+ if (!wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ)) {
+ dev_dbg(fsl_lpspi->dev, "wait for completion timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int fsl_lpspi_transfer_one(struct spi_controller *controller,
struct spi_device *spi,
struct spi_transfer *t)
{
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);
int ret;

fsl_lpspi->tx_buf = t->tx_buf;
@@ -330,13 +386,13 @@ static int fsl_lpspi_transfer_one(struct spi_master *master,
fsl_lpspi->remain = t->len;

reinit_completion(&fsl_lpspi->xfer_done);
+ fsl_lpspi->slave_aborted = false;
+
fsl_lpspi_write_tx_fifo(fsl_lpspi);

- ret = wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ);
- if (!ret) {
- dev_dbg(fsl_lpspi->dev, "wait for completion timeout\n");
- return -ETIMEDOUT;
- }
+ ret = fsl_lpspi_wait_for_completion(controller);
+ if (ret)
+ return ret;

ret = fsl_lpspi_txfifo_empty(fsl_lpspi);
if (ret)
@@ -347,10 +403,11 @@ static int fsl_lpspi_transfer_one(struct spi_master *master,
return 0;
}

-static int fsl_lpspi_transfer_one_msg(struct spi_master *master,
+static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller,
struct spi_message *msg)
{
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);
struct spi_device *spi = msg->spi;
struct spi_transfer *xfer;
bool is_first_xfer = true;
@@ -366,7 +423,7 @@ static int fsl_lpspi_transfer_one_msg(struct spi_master *master,

is_first_xfer = false;

- ret = fsl_lpspi_transfer_one(master, spi, xfer);
+ ret = fsl_lpspi_transfer_one(controller, spi, xfer);
if (ret < 0)
goto complete;

@@ -374,13 +431,15 @@ static int fsl_lpspi_transfer_one_msg(struct spi_master *master,
}

complete:
- /* de-assert SS, then finalize current message */
- temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
- temp &= ~TCR_CONTC;
- writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
+ if (!fsl_lpspi->is_slave) {
+ /* de-assert SS, then finalize current message */
+ temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
+ temp &= ~TCR_CONTC;
+ writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
+ }

msg->status = ret;
- spi_finalize_current_message(master);
+ spi_finalize_current_message(controller);

return ret;
}
@@ -410,30 +469,39 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
static int fsl_lpspi_probe(struct platform_device *pdev)
{
struct fsl_lpspi_data *fsl_lpspi;
- struct spi_master *master;
+ struct spi_controller *controller;
struct resource *res;
int ret, irq;
u32 temp;

- master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data));
- if (!master)
+ if (of_property_read_bool((&pdev->dev)->of_node, "spi-slave"))
+ controller = spi_alloc_slave(&pdev->dev,
+ sizeof(struct fsl_lpspi_data));
+ else
+ controller = spi_alloc_master(&pdev->dev,
+ sizeof(struct fsl_lpspi_data));
+
+ if (!controller)
return -ENOMEM;

- platform_set_drvdata(pdev, master);
+ platform_set_drvdata(pdev, controller);

- master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
- master->bus_num = pdev->id;
+ controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
+ controller->bus_num = pdev->id;

- fsl_lpspi = spi_master_get_devdata(master);
+ fsl_lpspi = spi_controller_get_devdata(controller);
fsl_lpspi->dev = &pdev->dev;
-
- master->transfer_one_message = fsl_lpspi_transfer_one_msg;
- master->prepare_transfer_hardware = lpspi_prepare_xfer_hardware;
- master->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware;
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
- master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
- master->dev.of_node = pdev->dev.of_node;
- master->bus_num = pdev->id;
+ fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node,
+ "spi-slave");
+
+ controller->transfer_one_message = fsl_lpspi_transfer_one_msg;
+ controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware;
+ controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware;
+ controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+ controller->dev.of_node = pdev->dev.of_node;
+ controller->bus_num = pdev->id;
+ controller->slave_abort = fsl_lpspi_slave_abort;

init_completion(&fsl_lpspi->xfer_done);

@@ -441,32 +509,32 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(fsl_lpspi->base)) {
ret = PTR_ERR(fsl_lpspi->base);
- goto out_master_put;
+ goto out_controller_put;
}

irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
- goto out_master_put;
+ goto out_controller_put;
}

ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0,
dev_name(&pdev->dev), fsl_lpspi);
if (ret) {
dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);
- goto out_master_put;
+ goto out_controller_put;
}

fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(fsl_lpspi->clk)) {
ret = PTR_ERR(fsl_lpspi->clk);
- goto out_master_put;
+ goto out_controller_put;
}

ret = clk_prepare_enable(fsl_lpspi->clk);
if (ret) {
dev_err(&pdev->dev, "can't enable lpspi clock, ret=%d\n", ret);
- goto out_master_put;
+ goto out_controller_put;
}

temp = readl(fsl_lpspi->base + IMX7ULP_PARAM);
@@ -475,24 +543,25 @@ static int fsl_lpspi_probe(struct platform_device *pdev)

clk_disable_unprepare(fsl_lpspi->clk);

- ret = devm_spi_register_master(&pdev->dev, master);
+ ret = devm_spi_register_controller(&pdev->dev, controller);
if (ret < 0) {
- dev_err(&pdev->dev, "spi_register_master error.\n");
- goto out_master_put;
+ dev_err(&pdev->dev, "spi_register_controller error.\n");
+ goto out_controller_put;
}

return 0;

-out_master_put:
- spi_master_put(master);
+out_controller_put:
+ spi_controller_put(controller);

return ret;
}

static int fsl_lpspi_remove(struct platform_device *pdev)
{
- struct spi_master *master = platform_get_drvdata(pdev);
- struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
+ struct spi_controller *controller = platform_get_drvdata(pdev);
+ struct fsl_lpspi_data *fsl_lpspi =
+ spi_controller_get_devdata(controller);

clk_disable_unprepare(fsl_lpspi->clk);

@@ -509,6 +578,6 @@ static struct platform_driver fsl_lpspi_driver = {
};
module_platform_driver(fsl_lpspi_driver);

-MODULE_DESCRIPTION("LPSPI Master Controller driver");
+MODULE_DESCRIPTION("LPSPI Controller driver");
MODULE_AUTHOR("Gao Pan <[email protected]>");
MODULE_LICENSE("GPL");
--
2.17.1



2018-10-24 08:00:56

by Clark Wang

[permalink] [raw]
Subject: [PATCH V2 3/5] spi: lpspi: Add 8qm/qxp support for lpspi

Add both ipg and per clock for lpspi to support i.MX8QM/QXP boards.

Signed-off-by: Xiaoning Wang <[email protected]>
---
V2:
- No changes.
---
drivers/spi/spi-fsl-lpspi.c | 52 +++++++++++++++++++++++++++++--------
1 file changed, 41 insertions(+), 11 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 79a3d4a82628..b51746c2de00 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -82,7 +82,8 @@ struct lpspi_config {
struct fsl_lpspi_data {
struct device *dev;
void __iomem *base;
- struct clk *clk;
+ struct clk *clk_ipg;
+ struct clk *clk_per;
bool is_slave;

void *rx_buf;
@@ -149,8 +150,19 @@ static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
{
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);
+ int ret;
+
+ ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fsl_lpspi->clk_per);
+ if (ret) {
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);
+ return ret;
+ }

- return clk_prepare_enable(fsl_lpspi->clk);
+ return 0;
}

static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
@@ -158,7 +170,8 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);

- clk_disable_unprepare(fsl_lpspi->clk);
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);
+ clk_disable_unprepare(fsl_lpspi->clk_per);

return 0;
}
@@ -242,7 +255,7 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
unsigned int perclk_rate, scldiv;
u8 prescale;

- perclk_rate = clk_get_rate(fsl_lpspi->clk);
+ perclk_rate = clk_get_rate(fsl_lpspi->clk_per);
for (prescale = 0; prescale < 8; prescale++) {
scldiv = perclk_rate /
(clkdivs[prescale] * config.speed_hz) - 2;
@@ -504,15 +517,30 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
goto out_controller_put;
}

- fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(fsl_lpspi->clk)) {
- ret = PTR_ERR(fsl_lpspi->clk);
+ fsl_lpspi->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(fsl_lpspi->clk_per)) {
+ ret = PTR_ERR(fsl_lpspi->clk_per);
+ goto out_controller_put;
+ }
+
+ fsl_lpspi->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(fsl_lpspi->clk_ipg)) {
+ ret = PTR_ERR(fsl_lpspi->clk_ipg);
+ goto out_controller_put;
+ }
+
+ ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't enable lpspi ipg clock, ret=%d\n", ret);
goto out_controller_put;
}

- ret = clk_prepare_enable(fsl_lpspi->clk);
+ ret = clk_prepare_enable(fsl_lpspi->clk_per);
if (ret) {
- dev_err(&pdev->dev, "can't enable lpspi clock, ret=%d\n", ret);
+ dev_err(&pdev->dev,
+ "can't enable lpspi per clock, ret=%d\n", ret);
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);
goto out_controller_put;
}

@@ -520,7 +548,8 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
fsl_lpspi->txfifosize = 1 << (temp & 0x0f);
fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f);

- clk_disable_unprepare(fsl_lpspi->clk);
+ clk_disable_unprepare(fsl_lpspi->clk_per);
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);

ret = devm_spi_register_controller(&pdev->dev, controller);
if (ret < 0) {
@@ -542,7 +571,8 @@ static int fsl_lpspi_remove(struct platform_device *pdev)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);

- clk_disable_unprepare(fsl_lpspi->clk);
+ clk_disable_unprepare(fsl_lpspi->clk_per);
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);

return 0;
}
--
2.17.1


2018-10-24 08:23:08

by Clark Wang

[permalink] [raw]
Subject: [PATCH V2 4/5] spi: lpspi: enable runtime pm for lpspi

Enable the runtime pm for lpspi module

BuildInfo:
- U-Boot 2018.03-imx_4.14.y

Signed-off-by: Han Xu <[email protected]>
Reviewed-by: Frank Li <[email protected]>
Signed-off-by: Xiaoning Wang <[email protected]>
---
V2:
- Add pinctrl/consumer.h include to fix the Warning error: implicit
declaration of function 'pinctrl_pm_select_sleep_state'.
---
drivers/spi/spi-fsl-lpspi.c | 117 ++++++++++++++++++++++++++++--------
1 file changed, 92 insertions(+), 25 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index b51746c2de00..1be29daa9f92 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -16,7 +16,9 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -24,6 +26,8 @@

#define DRIVER_NAME "fsl_lpspi"

+#define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */
+
/* i.MX7ULP LPSPI registers */
#define IMX7ULP_VERID 0x0
#define IMX7ULP_PARAM 0x4
@@ -152,13 +156,9 @@ static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
spi_controller_get_devdata(controller);
int ret;

- ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(fsl_lpspi->clk_per);
- if (ret) {
- clk_disable_unprepare(fsl_lpspi->clk_ipg);
+ ret = pm_runtime_get_sync(fsl_lpspi->dev);
+ if (ret < 0) {
+ dev_err(fsl_lpspi->dev, "failed to enable clock\n");
return ret;
}

@@ -170,8 +170,8 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);

- clk_disable_unprepare(fsl_lpspi->clk_ipg);
- clk_disable_unprepare(fsl_lpspi->clk_per);
+ pm_runtime_mark_last_busy(fsl_lpspi->dev);
+ pm_runtime_put_autosuspend(fsl_lpspi->dev);

return 0;
}
@@ -458,6 +458,45 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
return IRQ_NONE;
}

+int fsl_lpspi_runtime_resume(struct device *dev)
+{
+ struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(fsl_lpspi->clk_per);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
+ if (ret) {
+ clk_disable_unprepare(fsl_lpspi->clk_per);
+ return ret;
+ }
+
+ return 0;
+}
+
+int fsl_lpspi_runtime_suspend(struct device *dev)
+{
+ struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(fsl_lpspi->clk_per);
+ clk_disable_unprepare(fsl_lpspi->clk_ipg);
+
+ return 0;
+}
+
+static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi)
+{
+ struct device *dev = fsl_lpspi->dev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, FSL_LPSPI_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+
+ return 0;
+}
+
static int fsl_lpspi_probe(struct platform_device *pdev)
{
struct fsl_lpspi_data *fsl_lpspi;
@@ -483,6 +522,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)

fsl_lpspi = spi_controller_get_devdata(controller);
fsl_lpspi->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, fsl_lpspi);
fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node,
"spi-slave");

@@ -529,28 +569,21 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
goto out_controller_put;
}

- ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
- if (ret) {
- dev_err(&pdev->dev,
- "can't enable lpspi ipg clock, ret=%d\n", ret);
+ /* enable the clock */
+ ret = fsl_lpspi_init_rpm(fsl_lpspi);
+ if (ret)
goto out_controller_put;
- }

- ret = clk_prepare_enable(fsl_lpspi->clk_per);
- if (ret) {
- dev_err(&pdev->dev,
- "can't enable lpspi per clock, ret=%d\n", ret);
- clk_disable_unprepare(fsl_lpspi->clk_ipg);
- goto out_controller_put;
+ ret = pm_runtime_get_sync(fsl_lpspi->dev);
+ if (ret < 0) {
+ dev_err(fsl_lpspi->dev, "failed to enable clock\n");
+ return ret;
}

temp = readl(fsl_lpspi->base + IMX7ULP_PARAM);
fsl_lpspi->txfifosize = 1 << (temp & 0x0f);
fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f);

- clk_disable_unprepare(fsl_lpspi->clk_per);
- clk_disable_unprepare(fsl_lpspi->clk_ipg);
-
ret = devm_spi_register_controller(&pdev->dev, controller);
if (ret < 0) {
dev_err(&pdev->dev, "spi_register_controller error.\n");
@@ -571,16 +604,50 @@ static int fsl_lpspi_remove(struct platform_device *pdev)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);

- clk_disable_unprepare(fsl_lpspi->clk_per);
- clk_disable_unprepare(fsl_lpspi->clk_ipg);
+ pm_runtime_disable(fsl_lpspi->dev);
+
+ spi_master_put(controller);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_lpspi_suspend(struct device *dev)
+{
+ int ret;
+
+ pinctrl_pm_select_sleep_state(dev);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
+}
+
+static int fsl_lpspi_resume(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "Error in resume: %d\n", ret);
+ return ret;
+ }
+
+ pinctrl_pm_select_default_state(dev);

return 0;
}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_lpspi_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
+ fsl_lpspi_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
+};

static struct platform_driver fsl_lpspi_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = fsl_lpspi_dt_ids,
+ .pm = &fsl_lpspi_pm_ops,
},
.probe = fsl_lpspi_probe,
.remove = fsl_lpspi_remove,
--
2.17.1


2018-10-24 08:23:09

by Clark Wang

[permalink] [raw]
Subject: [PATCH V2 2/5] spi: lpspi: Improve the stability of lpspi data transmission

Use SR_TDF to judge if need send data, and SR_FCF to judge if
transmission end to replace the waiting after transmission end. This
waiting has no actual meaning, for the real end will set the FCF
flag.

Resolved an issue that could cause a transmission timeout when
transferring large amounts of data.

After making these changes, there is no need to use
fsl_lpspi_txfifo_empty(), so remove it.

Signed-off-by: Xiaoning Wang <[email protected]>
---
V2:
- Sorry for my negligence.
Fix an issue which happened when I split the patch from my work git
tree. Forgot to delete fsl_lpspi_txfifo_empty() code.
---
drivers/spi/spi-fsl-lpspi.c | 63 +++++++++++++------------------------
1 file changed, 21 insertions(+), 42 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 86cb38d98a39..79a3d4a82628 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -49,9 +49,11 @@
#define CR_RST BIT(1)
#define CR_MEN BIT(0)
#define SR_TCF BIT(10)
+#define SR_FCF BIT(9)
#define SR_RDF BIT(1)
#define SR_TDF BIT(0)
#define IER_TCIE BIT(10)
+#define IER_FCIE BIT(9)
#define IER_RDIE BIT(1)
#define IER_TDIE BIT(0)
#define CFGR1_PCSCFG BIT(27)
@@ -161,28 +163,10 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
return 0;
}

-static int fsl_lpspi_txfifo_empty(struct fsl_lpspi_data *fsl_lpspi)
-{
- u32 txcnt;
- unsigned long orig_jiffies = jiffies;
-
- do {
- txcnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff;
-
- if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
- dev_dbg(fsl_lpspi->dev, "txfifo empty timeout\n");
- return -ETIMEDOUT;
- }
- cond_resched();
-
- } while (txcnt);
-
- return 0;
-}
-
static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi)
{
u8 txfifo_cnt;
+ u32 temp;

txfifo_cnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff;

@@ -193,9 +177,15 @@ static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi)
txfifo_cnt++;
}

- if (!fsl_lpspi->remain && (txfifo_cnt < fsl_lpspi->txfifosize))
- writel(0, fsl_lpspi->base + IMX7ULP_TDR);
- else
+ if (txfifo_cnt < fsl_lpspi->txfifosize) {
+ if (!fsl_lpspi->is_slave) {
+ temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
+ temp &= ~TCR_CONTC;
+ writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
+ }
+
+ fsl_lpspi_intctrl(fsl_lpspi, IER_FCIE);
+ } else
fsl_lpspi_intctrl(fsl_lpspi, IER_TDIE);
}

@@ -394,12 +384,6 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller,
if (ret)
return ret;

- ret = fsl_lpspi_txfifo_empty(fsl_lpspi);
- if (ret)
- return ret;
-
- fsl_lpspi_read_rx_fifo(fsl_lpspi);
-
return 0;
}

@@ -411,7 +395,6 @@ static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller,
struct spi_device *spi = msg->spi;
struct spi_transfer *xfer;
bool is_first_xfer = true;
- u32 temp;
int ret = 0;

msg->status = 0;
@@ -431,13 +414,6 @@ static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller,
}

complete:
- if (!fsl_lpspi->is_slave) {
- /* de-assert SS, then finalize current message */
- temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
- temp &= ~TCR_CONTC;
- writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
- }
-
msg->status = ret;
spi_finalize_current_message(controller);

@@ -446,20 +422,23 @@ static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller,

static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
{
+ u32 temp_SR, temp_IER;
struct fsl_lpspi_data *fsl_lpspi = dev_id;
- u32 temp;

+ temp_IER = readl(fsl_lpspi->base + IMX7ULP_IER);
fsl_lpspi_intctrl(fsl_lpspi, 0);
- temp = readl(fsl_lpspi->base + IMX7ULP_SR);
+ temp_SR = readl(fsl_lpspi->base + IMX7ULP_SR);

fsl_lpspi_read_rx_fifo(fsl_lpspi);

- if (temp & SR_TDF) {
+ if ((temp_SR & SR_TDF) && (temp_IER & IER_TDIE)) {
fsl_lpspi_write_tx_fifo(fsl_lpspi);
+ return IRQ_HANDLED;
+ }

- if (!fsl_lpspi->remain)
- complete(&fsl_lpspi->xfer_done);
-
+ if (temp_SR & SR_FCF && (temp_IER & IER_FCIE)) {
+ writel(SR_FCF, fsl_lpspi->base + IMX7ULP_SR);
+ complete(&fsl_lpspi->xfer_done);
return IRQ_HANDLED;
}

--
2.17.1


2018-10-24 08:23:10

by Clark Wang

[permalink] [raw]
Subject: [PATCH V2 5/5] spi: lpspi: CLK pin becomes low when CR_RST=1

Remove Reset operation in fsl_lpspi_config(). This RST may cause both CLK
and CS pins go from high to low level under cs-gpio mode.
Add fsl_lpspi_reset() function after one message transfer to clear all
flags in use.

Signed-off-by: Xiaoning Wang <[email protected]>
Reviewed-by: Fugang Duan <[email protected]>
---
V2:
- Wrong place to remove fsl_lpspi_txfifo_empty() code when I split patch
from my work git tree Although the final code which after applying all
these five patches is correct.
---
drivers/spi/spi-fsl-lpspi.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 1be29daa9f92..3a7fe1c83aaa 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -282,10 +282,6 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
u32 temp;
int ret;

- temp = CR_RST;
- writel(temp, fsl_lpspi->base + IMX7ULP_CR);
- writel(0, fsl_lpspi->base + IMX7ULP_CR);
-
if (!fsl_lpspi->is_slave) {
ret = fsl_lpspi_set_bitrate(fsl_lpspi);
if (ret)
@@ -376,6 +372,21 @@ static int fsl_lpspi_wait_for_completion(struct spi_controller *controller)
return 0;
}

+static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi)
+{
+ u32 temp;
+
+ /* W1C for all flags in SR */
+ temp = 0x3F << 8;
+ writel(temp, fsl_lpspi->base + IMX7ULP_SR);
+
+ /* Clear FIFO and disable module */
+ temp = CR_RRF | CR_RTF;
+ writel(temp, fsl_lpspi->base + IMX7ULP_CR);
+
+ return 0;
+}
+
static int fsl_lpspi_transfer_one(struct spi_controller *controller,
struct spi_device *spi,
struct spi_transfer *t)
@@ -397,6 +408,8 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller,
if (ret)
return ret;

+ fsl_lpspi_reset(fsl_lpspi);
+
return 0;
}

--
2.17.1


2018-10-24 10:39:15

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH V2 3/5] spi: lpspi: Add 8qm/qxp support for lpspi

On Wed, Oct 24, 2018 at 07:58:27AM +0000, Clark Wang wrote:
> Add both ipg and per clock for lpspi to support i.MX8QM/QXP boards.

I'm missing both the cover letter and rest of the series so I don't know
what's going on with dependencies here - what's the story?

> Signed-off-by: Xiaoning Wang <[email protected]>

Please try to at least have the git commit author (which would show up
as an in-body From: if you use git send-email), especially given that
it's not just a spelling variation but actually a different name used in
both places it's really not clear at the minute that you're not
forwarding a patch at the minute, if that were the case there'd be a
missing Signed-off-by here.


Attachments:
(No filename) (709.00 B)
signature.asc (499.00 B)
Download all attachments

2018-10-24 10:45:40

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH V2 4/5] spi: lpspi: enable runtime pm for lpspi

On Wed, Oct 24, 2018 at 07:58:30AM +0000, Clark Wang wrote:

> Enable the runtime pm for lpspi module
>
> BuildInfo:
> - U-Boot 2018.03-imx_4.14.y
>
> Signed-off-by: Han Xu <[email protected]>
> Reviewed-by: Frank Li <[email protected]>
> Signed-off-by: Xiaoning Wang <[email protected]>

This looks like Han Xu wrote this patch, Frank Li reviewed it and then
you are sending it on but it doesn't say that you wrote it - is that
what's going on here?


Attachments:
(No filename) (468.00 B)
signature.asc (499.00 B)
Download all attachments

2018-10-24 10:57:57

by Fabio Estevam

[permalink] [raw]
Subject: Re: [PATCH V2 4/5] spi: lpspi: enable runtime pm for lpspi

Hi Clark,

On Wed, Oct 24, 2018 at 5:01 AM Clark Wang <[email protected]> wrote:
>
> Enable the runtime pm for lpspi module
>
> BuildInfo:
> - U-Boot 2018.03-imx_4.14.y

This buildinfo piece is not very helpful in upstream. Please drop it.

> Signed-off-by: Han Xu <[email protected]>
> Reviewed-by: Frank Li <[email protected]>
> Signed-off-by: Xiaoning Wang <[email protected]>

Also, it would be nice if your From field (Clark Wang) could match
your Signed-off-by (Xiaoning Wang).

As Mark mentioned in case this patch comes from Han Xu, then his name
should go into the From field instead.