summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorAllen Xu <b45815@freescale.com>2014-09-25 05:39:09 -0500
committerNitin Garg <nitin.garg@freescale.com>2015-09-17 09:20:59 -0500
commitb9a822a262e9f20fc4148f263ed835cdac9fb8a9 (patch)
tree63acbb9a9a89c00f6a3af3975cd3ece33ba52ab8 /drivers/mtd
parent29c4438789331410b6fc104ee43a4ad78c7f17dc (diff)
ENGR00311101 QSPI: i.MX6SX: fixed the random QSPI access failed issue
We found there is a low probability(5%) QSPI access timeout issue, usually it happened on kernel boot stage, the first time kernel tried to access QSPI chip. The READ_ID command was sent but not executed, consequently the probe function failed. Finally we located the issue by these steps. 1. Since the issue happened randomly and usually it cost half day to reproduce, we add more debug code in driver, to create a timeout file if the issue occurred. 2. Prepared an autorun script to keep rebooting the system and check if the timeout file existed, if the file existed, stop reboot. 3. The system will stop rebooting when timeout error occurred, set the CCM_CCOSR register and related IOMUX to measure QPSI clock, found there is no clock output, while clock output can be measured when QSPI driver successfully probed. 4. Check the code and found QSPI clock rate was changed while not disabled clock gate, most of the multiplexers on i.MX6 are glitch ones, clock glitch may occurred and propagated into downstream clock dividers Based on the new clock flag(CLK_SET_RATE_GATE) and new framework, we need to change the approach of seting clock rate. In current implementation, there are several places in which the clock was touched. 1. probe function. prepare and enable clock before setting the QSPI register, disable and unprepare the clock before exit. 2. nor_setup & nor_setup_last, since we change clock rate in these two functions. 3. fsl_qspi_prep and fsl_qspi_unprep, clock was enabled only when got QSPI access request. 4. resume function. Clock was required to restroe the setting after resume, disable the clock before exit. Signed-off-by: Allen Xu <b45815@freescale.com> (cherry picked from commit 04b31985952a4a8ba226d7bac474f70a95f5e674)
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c83
1 files changed, 57 insertions, 26 deletions
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index a7d6f00b46fd..fafc8ad52f38 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -606,7 +606,7 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
u32 reg, reg2;
int seqid;
- /* AHB configuration for access buffer 0/1/2 .*/
+ /* AHB configuration for access buffer 0/1/2 . */
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
@@ -649,6 +649,32 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
}
}
+/* This function was used to prepare and enable QSPI clock */
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
+{
+ int ret;
+
+ ret = clk_prepare_enable(q->clk_en);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(q->clk);
+ if (ret) {
+ clk_disable_unprepare(q->clk_en);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* This function was used to disable and unprepare QSPI clock */
+static int fsl_qspi_clk_disable_unprep(struct fsl_qspi *q)
+{
+ clk_disable_unprepare(q->clk);
+ clk_disable_unprepare(q->clk_en);
+
+}
+
/* We use this function to do some basic init for spi_nor_scan(). */
static int fsl_qspi_nor_setup(struct fsl_qspi *q)
{
@@ -656,11 +682,19 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
u32 reg;
int ret;
- /* the default frequency, we will change it in the future.*/
+ /* disable and unprepare clock first */
+ fsl_qspi_clk_disable_unprep(q);
+
+ /* the default frequency, we will change it in the future. */
ret = clk_set_rate(q->clk, 66000000);
if (ret)
return ret;
+ /* prepare and enable the clock */
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
/* Reset the module */
writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
base + QUADSPI_MCR);
@@ -698,10 +732,18 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
if (is_imx6sx_qspi(q))
rate *= 4;
+ /* disable and unprepare clock first */
+ fsl_qspi_clk_disable_unprep(q);
+
ret = clk_set_rate(q->clk, rate);
if (ret)
return ret;
+ /* prepare and enable the clock */
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
/* Init the LUT table again. */
fsl_qspi_init_lut(q);
@@ -828,16 +870,10 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
mutex_lock(&q->lock);
- ret = clk_enable(q->clk_en);
+ ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;
- ret = clk_enable(q->clk);
- if (ret) {
- clk_disable(q->clk_en);
- return ret;
- }
-
fsl_qspi_set_base_addr(q, nor);
return 0;
}
@@ -846,8 +882,7 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct fsl_qspi *q = nor->priv;
- clk_disable(q->clk);
- clk_disable(q->clk_en);
+ fsl_qspi_clk_disable_unprep(q);
mutex_unlock(&q->lock);
}
@@ -903,16 +938,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
goto map_failed;
}
- ret = clk_prepare_enable(q->clk_en);
- if (ret) {
- dev_err(dev, "can not enable the qspi_en clock\n");
- goto map_failed;
- }
-
- ret = clk_prepare_enable(q->clk);
+ ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
- clk_disable_unprepare(q->clk_en);
- dev_err(dev, "can not enable the qspi clock\n");
+ dev_err(dev, "can not enable the clock\n");
goto map_failed;
}
@@ -1031,8 +1059,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
goto last_init_failed;
- clk_disable(q->clk);
- clk_disable(q->clk_en);
+ fsl_qspi_clk_disable_unprep(q);
dev_info(dev, "QuadSPI SPI NOR flash driver\n");
return 0;
@@ -1041,8 +1068,7 @@ last_init_failed:
mtd_device_unregister(&q->mtd[i]);
irq_failed:
- clk_disable_unprepare(q->clk);
- clk_disable_unprepare(q->clk_en);
+ fsl_qspi_clk_disable_unprep(q);
map_failed:
dev_err(dev, "Freescale QuadSPI probe failed\n");
return ret;
@@ -1060,8 +1086,6 @@ static int fsl_qspi_remove(struct platform_device *pdev)
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER);
- clk_unprepare(q->clk);
- clk_unprepare(q->clk_en);
return 0;
}
@@ -1072,12 +1096,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
static int fsl_qspi_resume(struct platform_device *pdev)
{
+ int ret;
struct fsl_qspi *q = platform_get_drvdata(pdev);
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return ret;
+
fsl_qspi_nor_setup(q);
fsl_qspi_set_map_addr(q);
fsl_qspi_nor_setup_last(q);
+ fsl_qspi_clk_disable_unprep(q);
+
return 0;
}