2016-10-28 16:18:23 +00:00
|
|
|
From 2c5a3db21926e9ebfd7a32e3c36a3256ed84903c Mon Sep 17 00:00:00 2001
|
|
|
|
From: Hou Zhiqiang <Zhiqiang.Hou@freescale.com>
|
|
|
|
Date: Thu, 19 Nov 2015 20:25:24 +0800
|
|
|
|
Subject: [PATCH 090/113] mtd: spi-nor: Add SPI NOR layer PM support
|
|
|
|
|
|
|
|
[context adjustment]
|
|
|
|
|
|
|
|
Add the Power Management API in SPI NOR framework.
|
|
|
|
The Power Management system will turn off power supply to SPI flash
|
|
|
|
when system suspending, and then the SPI flash will be in the reset
|
|
|
|
state after system resuming. As a result, the status&configurations
|
|
|
|
of SPI flash driver will mismatch with its current hardware state.
|
|
|
|
So reinitialize SPI flash to make sure it is resumed to the correct
|
|
|
|
state.
|
|
|
|
And the SPI NOR layer just do common configuration depending on the
|
|
|
|
records in structure spi_nor.
|
|
|
|
|
|
|
|
Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@freescale.com>
|
|
|
|
Integrated-by: Jiang Yutang <yutang.jiang@nxp.com>
|
|
|
|
---
|
|
|
|
drivers/mtd/spi-nor/spi-nor.c | 74 ++++++++++++++++++++++++++++++++++-------
|
|
|
|
include/linux/mtd/spi-nor.h | 9 +++++
|
|
|
|
2 files changed, 71 insertions(+), 12 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/mtd/spi-nor/spi-nor.c
|
|
|
|
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
2016-11-01 11:21:15 +00:00
|
|
|
@@ -1140,6 +1140,26 @@ static int spi_nor_check(struct spi_nor
|
2016-10-28 16:18:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
|
|
|
+ * with the software protection bits set
|
|
|
|
+ */
|
|
|
|
+static int spi_nor_unprotect_on_powerup(struct spi_nor *nor)
|
|
|
|
+{
|
|
|
|
+ const struct flash_info *info = NULL;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ info = spi_nor_read_id(nor);
|
|
|
|
+ if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
|
|
|
|
+ JEDEC_MFR(info) == SNOR_MFR_INTEL ||
|
|
|
|
+ JEDEC_MFR(info) == SNOR_MFR_SST) {
|
|
|
|
+ write_enable(nor);
|
|
|
|
+ ret = write_sr(nor, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
|
|
|
{
|
|
|
|
const struct flash_info *info = NULL;
|
2016-11-01 11:21:15 +00:00
|
|
|
@@ -1187,18 +1207,9 @@ int spi_nor_scan(struct spi_nor *nor, co
|
2016-10-28 16:18:23 +00:00
|
|
|
|
|
|
|
mutex_init(&nor->lock);
|
|
|
|
|
|
|
|
- /*
|
|
|
|
- * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
|
|
|
- * with the software protection bits set
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
|
|
|
|
- JEDEC_MFR(info) == SNOR_MFR_INTEL ||
|
|
|
|
- JEDEC_MFR(info) == SNOR_MFR_MACRONIX ||
|
|
|
|
- JEDEC_MFR(info) == SNOR_MFR_SST) {
|
|
|
|
- write_enable(nor);
|
|
|
|
- write_sr(nor, 0);
|
|
|
|
- }
|
|
|
|
+ ret = spi_nor_unprotect_on_powerup(nor);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
|
|
|
|
if (!mtd->name)
|
|
|
|
mtd->name = dev_name(dev);
|
2016-11-01 11:21:15 +00:00
|
|
|
@@ -1364,6 +1375,45 @@ int spi_nor_scan(struct spi_nor *nor, co
|
2016-10-28 16:18:23 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(spi_nor_scan);
|
|
|
|
|
|
|
|
+static int spi_nor_hw_reinit(struct spi_nor *nor)
|
|
|
|
+{
|
|
|
|
+ const struct flash_info *info = NULL;
|
|
|
|
+ struct device *dev = nor->dev;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ info = spi_nor_read_id(nor);
|
|
|
|
+
|
|
|
|
+ ret = spi_nor_unprotect_on_powerup(nor);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ if (nor->flash_read == SPI_NOR_QUAD) {
|
|
|
|
+ ret = set_quad_mode(nor, info);
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_err(dev, "quad mode not supported\n");
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (nor->addr_width == 4 &&
|
|
|
|
+ JEDEC_MFR(info) != SNOR_MFR_SPANSION)
|
|
|
|
+ set_4byte(nor, info, 1);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int spi_nor_suspend(struct spi_nor *nor)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(spi_nor_suspend);
|
|
|
|
+
|
|
|
|
+int spi_nor_resume(struct spi_nor *nor)
|
|
|
|
+{
|
|
|
|
+ return spi_nor_hw_reinit(nor);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(spi_nor_resume);
|
|
|
|
+
|
|
|
|
static const struct flash_info *spi_nor_match_id(const char *name)
|
|
|
|
{
|
|
|
|
const struct flash_info *id = spi_nor_ids;
|
|
|
|
--- a/include/linux/mtd/spi-nor.h
|
|
|
|
+++ b/include/linux/mtd/spi-nor.h
|
|
|
|
@@ -210,4 +210,13 @@ static inline struct device_node *spi_no
|
|
|
|
*/
|
|
|
|
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * spi_nor_suspend/resume() - the SPI NOR layer PM API
|
|
|
|
+ * @nor: the spi_nor structure
|
|
|
|
+ *
|
|
|
|
+ * Return: 0 for success, others for failure.
|
|
|
|
+ */
|
|
|
|
+int spi_nor_suspend(struct spi_nor *nor);
|
|
|
|
+int spi_nor_resume(struct spi_nor *nor);
|
|
|
|
+
|
|
|
|
#endif
|