summaryrefslogtreecommitdiff
path: root/drivers/mtd/devices/m25p80.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/devices/m25p80.c')
-rw-r--r--drivers/mtd/devices/m25p80.c225
1 files changed, 190 insertions, 35 deletions
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 2f3d2a5ff34..c9c71e0a6cc 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -31,10 +31,15 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
+#ifdef CONFIG_M25PXX_M4SENSORHUB_CB
+#include <linux/m4sensorhub.h>
+#endif
+
/* Flash opcodes. */
#define OPCODE_WREN 0x06 /* Write enable */
#define OPCODE_RDSR 0x05 /* Read status register */
@@ -77,6 +82,12 @@
/****************************************************************************/
+enum {
+ M25P_OFF,
+ M25P_ON,
+ M25P_MODE_MAX
+};
+
struct m25p {
struct spi_device *spi;
struct mutex lock;
@@ -86,6 +97,13 @@ struct m25p {
u8 erase_opcode;
u8 *command;
bool fast_read;
+#ifdef CONFIG_M25PXX_M4SENSORHUB_CB
+ bool m4sensorhub_cb;
+#endif
+ struct pinctrl *pctrl;
+ struct pinctrl_state *pctrl_states[M25P_MODE_MAX];
+ int enable_gpio;
+ enum of_gpio_flags enable_gpio_flags;
};
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -93,6 +111,11 @@ static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
return container_of(mtd, struct m25p, mtd);
}
+const char *m25p_pin_state_labels[M25P_MODE_MAX] = {
+ "off",
+ "on"
+};
+
/****************************************************************************/
/*
@@ -655,6 +678,41 @@ err: mutex_unlock(&flash->lock);
return res;
}
+static int m25p80_get_device(struct mtd_info *mtd)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+
+ if (!mtd->usecount) {
+ if (flash->pctrl)
+ pinctrl_select_state(flash->pctrl,
+ flash->pctrl_states[M25P_ON]);
+ if (gpio_is_valid(flash->enable_gpio)) {
+ gpio_direction_output(flash->enable_gpio, 1);
+ msleep(10);
+ }
+ }
+
+ return 0;
+}
+
+static void m25p80_put_device(struct mtd_info *mtd)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+
+ if (!mtd->usecount) {
+ if (flash->pctrl) {
+ /* if pinctrl is used, set gpio to input */
+ if (gpio_is_valid(flash->enable_gpio))
+ gpio_direction_input(flash->enable_gpio);
+
+ pinctrl_select_state(flash->pctrl,
+ flash->pctrl_states[M25P_OFF]);
+ } else if (gpio_is_valid(flash->enable_gpio)) {
+ gpio_set_value(flash->enable_gpio, 0);
+ }
+ }
+}
+
/****************************************************************************/
/*
@@ -894,27 +952,57 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
return ERR_PTR(-ENODEV);
}
+static int m25p_pin_setup(struct device *dev, struct m25p *flash)
+{
+ int i, ret = 0;
+
+ flash->pctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(flash->pctrl)) {
+ ret = PTR_ERR(flash->pctrl);
+ flash->pctrl = NULL;
+ dev_warn(dev, "no pinctrl handle\n");
+ return ret;
+ }
+
+ for (i = 0; !ret && (i < M25P_MODE_MAX); i++) {
+ flash->pctrl_states[i] = pinctrl_lookup_state(flash->pctrl,
+ m25p_pin_state_labels[i]);
+ if (IS_ERR(flash->pctrl_states[i])) {
+ ret = PTR_ERR(flash->pctrl_states[i]);
+ devm_pinctrl_put(flash->pctrl);
+ flash->pctrl = NULL;
+ dev_warn(dev, "no %s pinctrl state\n",
+ m25p_pin_state_labels[i]);
+ return ret;
+ }
+ }
+
+ if (!ret) {
+ ret = pinctrl_select_state(flash->pctrl,
+ flash->pctrl_states[M25P_ON]);
+ if (ret)
+ dev_warn(dev, "failed to activate %s pinctrl state\n",
+ m25p_pin_state_labels[M25P_ON]);
+ }
+
+ return ret;
+}
/*
* board specific setup should have ensured the SPI clock used here
* matches what the READ command supports, at least until this driver
* understands FAST_READ (for clocks over 25 MHz).
*/
-static int m25p_probe(struct spi_device *spi)
+static int m25p_init(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct flash_platform_data *data;
- struct m25p *flash;
+ struct m25p *flash = dev_get_drvdata(&spi->dev);
struct flash_info *info;
unsigned i;
struct mtd_part_parser_data ppdata;
struct device_node __maybe_unused *np = spi->dev.of_node;
-#ifdef CONFIG_MTD_OF_PARTS
- if (!of_device_is_available(np))
- return -ENODEV;
-#endif
-
/* Platform data helps sort out which chip type we have, as
* well as how this board partitions it. If we don't have
* a chip ID, try the JEDEC id commands; they'll work for most
@@ -960,20 +1048,6 @@ static int m25p_probe(struct spi_device *spi)
}
}
- flash = kzalloc(sizeof *flash, GFP_KERNEL);
- if (!flash)
- return -ENOMEM;
- flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0),
- GFP_KERNEL);
- if (!flash->command) {
- kfree(flash);
- return -ENOMEM;
- }
-
- flash->spi = spi;
- mutex_init(&flash->lock);
- dev_set_drvdata(&spi->dev, flash);
-
/*
* Atmel, SST and Intel/Numonyx serial flash tend to power
* up with the software protection bits set
@@ -997,6 +1071,8 @@ static int m25p_probe(struct spi_device *spi)
flash->mtd.size = info->sector_size * info->n_sectors;
flash->mtd._erase = m25p80_erase;
flash->mtd._read = m25p80_read;
+ flash->mtd._get_device = m25p80_get_device;
+ flash->mtd._put_device = m25p80_put_device;
/* flash protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
@@ -1027,16 +1103,6 @@ static int m25p_probe(struct spi_device *spi)
flash->page_size = info->page_size;
flash->mtd.writebufsize = flash->page_size;
- flash->fast_read = false;
-#ifdef CONFIG_OF
- if (np && of_property_read_bool(np, "m25p,fast-read"))
- flash->fast_read = true;
-#endif
-
-#ifdef CONFIG_M25PXX_USE_FAST_READ
- flash->fast_read = true;
-#endif
-
if (info->addr_width)
flash->addr_width = info->addr_width;
else {
@@ -1048,6 +1114,17 @@ static int m25p_probe(struct spi_device *spi)
flash->addr_width = 3;
}
+ if (flash->pctrl) {
+ /* if pinctrl is used, set gpio to input instead of low */
+ if (gpio_is_valid(flash->enable_gpio))
+ gpio_direction_input(flash->enable_gpio);
+
+ pinctrl_select_state(flash->pctrl,
+ flash->pctrl_states[M25P_OFF]);
+ } else if (gpio_is_valid(flash->enable_gpio)) {
+ gpio_set_value(flash->enable_gpio, 0);
+ }
+
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
(long long)flash->mtd.size >> 10);
@@ -1077,6 +1154,83 @@ static int m25p_probe(struct spi_device *spi)
data ? data->nr_parts : 0);
}
+#ifdef CONFIG_M25PXX_M4SENSORHUB_CB
+static int m25p_finish_init(struct init_calldata *p_arg)
+{
+ return m25p_init((struct spi_device *)p_arg->p_data);
+}
+#endif
+
+static int m25p_probe(struct spi_device *spi)
+{
+ int err;
+ struct m25p *flash;
+ struct device_node __maybe_unused *np = spi->dev.of_node;
+
+#ifdef CONFIG_MTD_OF_PARTS
+ if (!of_device_is_available(np))
+ return -ENODEV;
+#endif
+ flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+ if (!flash)
+ return -ENOMEM;
+
+#ifdef CONFIG_M25PXX_USE_FAST_READ
+ flash->fast_read = true;
+#endif
+
+#ifdef CONFIG_OF
+ if (np && of_property_read_bool(np, "m25p,fast-read"))
+ flash->fast_read = true;
+#endif
+
+ flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE +
+ (flash->fast_read ? 1 : 0),
+ GFP_KERNEL);
+ if (!flash->command)
+ return -ENOMEM;
+
+ err = m25p_pin_setup(&spi->dev, flash);
+ if (err)
+ dev_warn(&spi->dev, "%s: c55_ctrl_pin_setup failed.\n",
+ __func__);
+
+#ifdef CONFIG_OF
+ flash->enable_gpio = of_get_gpio_flags(spi->dev.of_node, 0,
+ &(flash->enable_gpio_flags));
+ if (!gpio_is_valid(flash->enable_gpio)) {
+ dev_warn(&spi->dev, "%s: of_get_gpio failed: %d\n", __func__,
+ flash->enable_gpio);
+ } else {
+ gpio_request_one(flash->enable_gpio, flash->enable_gpio_flags,
+ "m25p_enable");
+ msleep(10);
+ }
+#else
+ flash->enable_gpio = -1;
+#endif
+
+ flash->spi = spi;
+ mutex_init(&flash->lock);
+ dev_set_drvdata(&spi->dev, flash);
+
+#ifdef CONFIG_M25PXX_M4SENSORHUB_CB
+ flash->m4sensorhub_cb = true;
+#ifdef CONFIG_OF
+ if (!np || !of_property_read_bool(np, "m25p,m4sensorhub_cb"))
+ flash->m4sensorhub_cb = false;
+#endif
+ if (flash->m4sensorhub_cb)
+ err = m4sensorhub_register_initcall(m25p_finish_init, spi);
+ else
+#endif
+ err = m25p_init(spi);
+
+ if (err)
+ dev_err(&spi->dev, "init failed.\n");
+
+ return err;
+}
static int m25p_remove(struct spi_device *spi)
{
@@ -1085,10 +1239,11 @@ static int m25p_remove(struct spi_device *spi)
/* Clean up MTD stuff. */
status = mtd_device_unregister(&flash->mtd);
- if (status == 0) {
- kfree(flash->command);
- kfree(flash);
- }
+
+#ifdef CONFIG_M25PXX_M4SENSORHUB_CB
+ if (flash->m4sensorhub_cb)
+ m4sensorhub_unregister_initcall(m25p_finish_init);
+#endif
return 0;
}