1 From 7723e59d483a883578115a73eb87eb7fff0ff724 Mon Sep 17 00:00:00 2001
2 From: Ezequiel Garcia <ezequiel.garcia@imgtec.com>
3 Date: Tue, 28 Feb 2017 10:37:24 +0000
4 Subject: mtd: spi-nand: Support Gigadevice GD5F
6 This commit uses the recently introduced SPI NAND framework to support
7 the Gigadevice GD5F serial NAND device.
9 The current support includes:
11 * Page read and page program operations (using on-die ECC)
12 * Page out-of-band read
15 * Device status retrieval
18 (based on http://lists.infradead.org/pipermail/linux-mtd/2014-December/056769.html)
20 Signed-off-by: Ezequiel Garcia <ezequiel.garcia@imgtec.com>
21 Signed-off-by: Ian Pozella <Ian.Pozella@imgtec.com>
23 drivers/mtd/spi-nand/Kconfig | 10 +
24 drivers/mtd/spi-nand/Makefile | 1 +
25 drivers/mtd/spi-nand/spi-nand-device.c | 472 +++++++++++++++++++++++++++++++++
26 3 files changed, 483 insertions(+)
27 create mode 100644 drivers/mtd/spi-nand/spi-nand-device.c
29 diff --git a/drivers/mtd/spi-nand/Kconfig b/drivers/mtd/spi-nand/Kconfig
30 index 17b31e1..ab6bb6c 100644
31 --- a/drivers/mtd/spi-nand/Kconfig
32 +++ b/drivers/mtd/spi-nand/Kconfig
33 @@ -5,3 +5,13 @@ menuconfig MTD_SPI_NAND
35 This is the framework for the SPI NAND.
39 +config MTD_SPI_NAND_DEVICES
40 + tristate "Support for SPI NAND devices"
42 + depends on MTD_SPI_NAND
44 + Select this option if you require support for SPI NAND devices.
47 diff --git a/drivers/mtd/spi-nand/Makefile b/drivers/mtd/spi-nand/Makefile
48 index d454c52..6e460d1 100644
49 --- a/drivers/mtd/spi-nand/Makefile
50 +++ b/drivers/mtd/spi-nand/Makefile
52 obj-$(CONFIG_MTD_SPI_NAND) += spi-nand-base.o
53 +obj-$(CONFIG_MTD_SPI_NAND_DEVICES) += spi-nand-device.o
54 diff --git a/drivers/mtd/spi-nand/spi-nand-device.c b/drivers/mtd/spi-nand/spi-nand-device.c
56 index 0000000..95db20b
58 +++ b/drivers/mtd/spi-nand/spi-nand-device.c
61 + * Copyright (C) 2014 Imagination Technologies Ltd.
63 + * This program is free software; you can redistribute it and/or modify
64 + * it under the terms of the GNU General Public License as published by
65 + * the Free Software Foundation; version 2 of the License.
68 + * 1. We avoid using a stack-allocated buffer for SPI messages. Using
69 + * a kmalloced buffer is probably better, given we shouldn't assume
70 + * any particular usage by SPI core.
73 +#include <linux/device.h>
74 +#include <linux/err.h>
75 +#include <linux/errno.h>
76 +#include <linux/module.h>
77 +#include <linux/mtd/mtd.h>
78 +#include <linux/mtd/partitions.h>
79 +#include <linux/mtd/spi-nand.h>
80 +#include <linux/sizes.h>
81 +#include <linux/spi/spi.h>
83 +/* SPI NAND commands */
84 +#define SPI_NAND_WRITE_ENABLE 0x06
85 +#define SPI_NAND_WRITE_DISABLE 0x04
86 +#define SPI_NAND_GET_FEATURE 0x0f
87 +#define SPI_NAND_SET_FEATURE 0x1f
88 +#define SPI_NAND_PAGE_READ 0x13
89 +#define SPI_NAND_READ_CACHE 0x03
90 +#define SPI_NAND_FAST_READ_CACHE 0x0b
91 +#define SPI_NAND_READ_CACHE_X2 0x3b
92 +#define SPI_NAND_READ_CACHE_X4 0x6b
93 +#define SPI_NAND_READ_CACHE_DUAL_IO 0xbb
94 +#define SPI_NAND_READ_CACHE_QUAD_IO 0xeb
95 +#define SPI_NAND_READ_ID 0x9f
96 +#define SPI_NAND_PROGRAM_LOAD 0x02
97 +#define SPI_NAND_PROGRAM_LOAD4 0x32
98 +#define SPI_NAND_PROGRAM_EXEC 0x10
99 +#define SPI_NAND_PROGRAM_LOAD_RANDOM 0x84
100 +#define SPI_NAND_PROGRAM_LOAD_RANDOM4 0xc4
101 +#define SPI_NAND_BLOCK_ERASE 0xd8
102 +#define SPI_NAND_RESET 0xff
104 +#define SPI_NAND_GD5F_READID_LEN 2
106 +#define SPI_NAND_GD5F_ECC_MASK (BIT(0) | BIT(1) | BIT(2))
107 +#define SPI_NAND_GD5F_ECC_UNCORR (BIT(0) | BIT(1) | BIT(2))
108 +#define SPI_NAND_GD5F_ECC_SHIFT 4
110 +static int spi_nand_gd5f_ooblayout_256_ecc(struct mtd_info *mtd, int section,
111 + struct mtd_oob_region *oobregion)
116 + oobregion->offset = 128;
117 + oobregion->length = 128;
122 +static int spi_nand_gd5f_ooblayout_256_free(struct mtd_info *mtd, int section,
123 + struct mtd_oob_region *oobregion)
128 + oobregion->offset = 1;
129 + oobregion->length = 127;
134 +static const struct mtd_ooblayout_ops spi_nand_gd5f_oob_256_ops = {
135 + .ecc = spi_nand_gd5f_ooblayout_256_ecc,
136 + .free = spi_nand_gd5f_ooblayout_256_free,
139 +static struct nand_flash_dev spi_nand_flash_ids[] = {
141 + .name = "SPI NAND 512MiB 3,3V",
142 + .id = { NAND_MFR_GIGADEVICE, 0xb4 },
145 + .erasesize = SZ_256K,
148 + .ecc.strength_ds = 8,
149 + .ecc.step_ds = 512,
152 + .name = "SPI NAND 512MiB 1,8V",
153 + .id = { NAND_MFR_GIGADEVICE, 0xa4 },
156 + .erasesize = SZ_256K,
159 + .ecc.strength_ds = 8,
160 + .ecc.step_ds = 512,
164 +enum spi_nand_device_variant {
169 +struct spi_nand_device_cmd {
172 + * Command and address. I/O errors have been observed if a
173 + * separate spi_transfer is used for command and address,
174 + * so keep them together.
190 +struct spi_nand_device {
191 + struct spi_nand spi_nand;
192 + struct spi_device *spi;
194 + struct spi_nand_device_cmd cmd;
197 +static int spi_nand_send_command(struct spi_device *spi,
198 + struct spi_nand_device_cmd *cmd)
200 + struct spi_message message;
201 + struct spi_transfer x[2];
204 + dev_err(&spi->dev, "cannot send an empty command\n");
208 + if (cmd->n_tx && cmd->n_rx) {
209 + dev_err(&spi->dev, "cannot send and receive data at the same time\n");
213 + spi_message_init(&message);
214 + memset(x, 0, sizeof(x));
216 + /* Command and address */
217 + x[0].len = cmd->n_cmd;
218 + x[0].tx_buf = cmd->cmd;
219 + x[0].tx_nbits = cmd->tx_nbits;
220 + spi_message_add_tail(&x[0], &message);
222 + /* Data to be transmitted */
224 + x[1].len = cmd->n_tx;
225 + x[1].tx_buf = cmd->tx_buf;
226 + x[1].tx_nbits = cmd->tx_nbits;
227 + spi_message_add_tail(&x[1], &message);
230 + /* Data to be received */
232 + x[1].len = cmd->n_rx;
233 + x[1].rx_buf = cmd->rx_buf;
234 + x[1].rx_nbits = cmd->rx_nbits;
235 + spi_message_add_tail(&x[1], &message);
238 + return spi_sync(spi, &message);
241 +static int spi_nand_device_reset(struct spi_nand *snand)
243 + struct spi_nand_device *snand_dev = snand->priv;
244 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
246 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
248 + cmd->cmd[0] = SPI_NAND_RESET;
250 + dev_dbg(snand->dev, "%s\n", __func__);
252 + return spi_nand_send_command(snand_dev->spi, cmd);
255 +static int spi_nand_device_read_reg(struct spi_nand *snand, u8 opcode, u8 *buf)
257 + struct spi_nand_device *snand_dev = snand->priv;
258 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
260 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
262 + cmd->cmd[0] = SPI_NAND_GET_FEATURE;
263 + cmd->cmd[1] = opcode;
267 + dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode);
269 + return spi_nand_send_command(snand_dev->spi, cmd);
272 +static int spi_nand_device_write_reg(struct spi_nand *snand, u8 opcode, u8 *buf)
274 + struct spi_nand_device *snand_dev = snand->priv;
275 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
277 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
279 + cmd->cmd[0] = SPI_NAND_SET_FEATURE;
280 + cmd->cmd[1] = opcode;
284 + dev_dbg(snand->dev, "%s: reg 0%x\n", __func__, opcode);
286 + return spi_nand_send_command(snand_dev->spi, cmd);
289 +static int spi_nand_device_write_enable(struct spi_nand *snand)
291 + struct spi_nand_device *snand_dev = snand->priv;
292 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
294 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
296 + cmd->cmd[0] = SPI_NAND_WRITE_ENABLE;
298 + dev_dbg(snand->dev, "%s\n", __func__);
300 + return spi_nand_send_command(snand_dev->spi, cmd);
303 +static int spi_nand_device_write_disable(struct spi_nand *snand)
305 + struct spi_nand_device *snand_dev = snand->priv;
306 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
308 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
310 + cmd->cmd[0] = SPI_NAND_WRITE_DISABLE;
312 + dev_dbg(snand->dev, "%s\n", __func__);
314 + return spi_nand_send_command(snand_dev->spi, cmd);
317 +static int spi_nand_device_write_page(struct spi_nand *snand,
318 + unsigned int page_addr)
320 + struct spi_nand_device *snand_dev = snand->priv;
321 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
323 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
325 + cmd->cmd[0] = SPI_NAND_PROGRAM_EXEC;
326 + cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
327 + cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
328 + cmd->cmd[3] = (u8)(page_addr & 0xff);
330 + dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr);
332 + return spi_nand_send_command(snand_dev->spi, cmd);
335 +static int spi_nand_device_store_cache(struct spi_nand *snand,
336 + unsigned int page_offset, size_t length,
339 + struct spi_nand_device *snand_dev = snand->priv;
340 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
341 + struct spi_device *spi = snand_dev->spi;
343 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
345 + cmd->cmd[0] = spi->mode & SPI_TX_QUAD ? SPI_NAND_PROGRAM_LOAD4 :
346 + SPI_NAND_PROGRAM_LOAD;
347 + cmd->cmd[1] = (u8)((page_offset & 0xff00) >> 8);
348 + cmd->cmd[2] = (u8)(page_offset & 0xff);
349 + cmd->n_tx = length;
350 + cmd->tx_buf = write_buf;
351 + cmd->tx_nbits = spi->mode & SPI_TX_QUAD ? 4 : 1;
353 + dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset);
355 + return spi_nand_send_command(snand_dev->spi, cmd);
358 +static int spi_nand_device_load_page(struct spi_nand *snand,
359 + unsigned int page_addr)
361 + struct spi_nand_device *snand_dev = snand->priv;
362 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
364 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
366 + cmd->cmd[0] = SPI_NAND_PAGE_READ;
367 + cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
368 + cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
369 + cmd->cmd[3] = (u8)(page_addr & 0xff);
371 + dev_dbg(snand->dev, "%s: page 0x%x\n", __func__, page_addr);
373 + return spi_nand_send_command(snand_dev->spi, cmd);
376 +static int spi_nand_device_read_cache(struct spi_nand *snand,
377 + unsigned int page_offset, size_t length,
380 + struct spi_nand_device *snand_dev = snand->priv;
381 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
382 + struct spi_device *spi = snand_dev->spi;
384 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
385 + if ((spi->mode & SPI_RX_DUAL) || (spi->mode & SPI_RX_QUAD))
389 + cmd->cmd[0] = (spi->mode & SPI_RX_QUAD) ? SPI_NAND_READ_CACHE_X4 :
390 + ((spi->mode & SPI_RX_DUAL) ? SPI_NAND_READ_CACHE_X2 :
391 + SPI_NAND_READ_CACHE);
392 + cmd->cmd[1] = 0; /* dummy byte */
393 + cmd->cmd[2] = (u8)((page_offset & 0xff00) >> 8);
394 + cmd->cmd[3] = (u8)(page_offset & 0xff);
395 + cmd->cmd[4] = 0; /* dummy byte */
396 + cmd->n_rx = length;
397 + cmd->rx_buf = read_buf;
398 + cmd->rx_nbits = (spi->mode & SPI_RX_QUAD) ? 4 :
399 + ((spi->mode & SPI_RX_DUAL) ? 2 : 1);
401 + dev_dbg(snand->dev, "%s: offset 0x%x\n", __func__, page_offset);
403 + return spi_nand_send_command(snand_dev->spi, cmd);
406 +static int spi_nand_device_block_erase(struct spi_nand *snand,
407 + unsigned int page_addr)
409 + struct spi_nand_device *snand_dev = snand->priv;
410 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
412 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
414 + cmd->cmd[0] = SPI_NAND_BLOCK_ERASE;
415 + cmd->cmd[1] = (u8)((page_addr & 0xff0000) >> 16);
416 + cmd->cmd[2] = (u8)((page_addr & 0xff00) >> 8);
417 + cmd->cmd[3] = (u8)(page_addr & 0xff);
419 + dev_dbg(snand->dev, "%s: block 0x%x\n", __func__, page_addr);
421 + return spi_nand_send_command(snand_dev->spi, cmd);
424 +static int spi_nand_gd5f_read_id(struct spi_nand *snand, u8 *buf)
426 + struct spi_nand_device *snand_dev = snand->priv;
427 + struct spi_nand_device_cmd *cmd = &snand_dev->cmd;
429 + memset(cmd, 0, sizeof(struct spi_nand_device_cmd));
431 + cmd->cmd[0] = SPI_NAND_READ_ID;
432 + cmd->n_rx = SPI_NAND_GD5F_READID_LEN;
435 + dev_dbg(snand->dev, "%s\n", __func__);
437 + return spi_nand_send_command(snand_dev->spi, cmd);
440 +static void spi_nand_gd5f_ecc_status(unsigned int status,
441 + unsigned int *corrected,
442 + unsigned int *ecc_error)
444 + unsigned int ecc_status = (status >> SPI_NAND_GD5F_ECC_SHIFT) &
445 + SPI_NAND_GD5F_ECC_MASK;
447 + *ecc_error = (ecc_status == SPI_NAND_GD5F_ECC_UNCORR) ? 1 : 0;
448 + if (*ecc_error == 0)
449 + *corrected = (ecc_status > 1) ? (2 + ecc_status) : 0;
452 +static int spi_nand_device_probe(struct spi_device *spi)
454 + enum spi_nand_device_variant variant;
455 + struct spi_nand_device *priv;
456 + struct spi_nand *snand;
459 + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
463 + snand = &priv->spi_nand;
465 + snand->read_cache = spi_nand_device_read_cache;
466 + snand->load_page = spi_nand_device_load_page;
467 + snand->store_cache = spi_nand_device_store_cache;
468 + snand->write_page = spi_nand_device_write_page;
469 + snand->write_reg = spi_nand_device_write_reg;
470 + snand->read_reg = spi_nand_device_read_reg;
471 + snand->block_erase = spi_nand_device_block_erase;
472 + snand->reset = spi_nand_device_reset;
473 + snand->write_enable = spi_nand_device_write_enable;
474 + snand->write_disable = spi_nand_device_write_disable;
475 + snand->dev = &spi->dev;
476 + snand->priv = priv;
478 + /* This'll mean we won't need to specify any specific compatible string
479 + * for a given device, and instead just support spi-nand.
481 + variant = spi_get_device_id(spi)->driver_data;
483 + case SPI_NAND_GD5F:
484 + snand->read_id = spi_nand_gd5f_read_id;
485 + snand->get_ecc_status = spi_nand_gd5f_ecc_status;
486 + snand->ooblayout = &spi_nand_gd5f_oob_256_ops;
489 + dev_err(snand->dev, "unknown device\n");
493 + spi_set_drvdata(spi, snand);
496 + ret = spi_nand_register(snand, spi_nand_flash_ids);
502 +static int spi_nand_device_remove(struct spi_device *spi)
504 + struct spi_nand *snand = spi_get_drvdata(spi);
506 + spi_nand_unregister(snand);
511 +const struct spi_device_id spi_nand_id_table[] = {
512 + { "spi-nand", SPI_NAND_GENERIC },
513 + { "gd5f", SPI_NAND_GD5F },
516 +MODULE_DEVICE_TABLE(spi, spi_nand_id_table);
518 +static struct spi_driver spi_nand_device_driver = {
520 + .name = "spi_nand_device",
521 + .owner = THIS_MODULE,
523 + .id_table = spi_nand_id_table,
524 + .probe = spi_nand_device_probe,
525 + .remove = spi_nand_device_remove,
527 +module_spi_driver(spi_nand_device_driver);
529 +MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@imgtec.com>");
530 +MODULE_DESCRIPTION("SPI NAND device support");
531 +MODULE_LICENSE("GPL v2");