Commit 6f041ccabb03bea16c2f21f3254dc9c1cb38425c

Authored by Boris Brezillon
Committed by Jagan Teki
1 parent 3181c0a622

mtd: spinand: Add initial support for the MX35LF1GE4AB chip

Add minimal support for the MX35LF1GE4AB SPI NAND chip.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Acked-by: Jagan Teki <jagan@openedev.com>

Showing 4 changed files with 141 additions and 1 deletions Side-by-side Diff

drivers/mtd/nand/spi/Makefile
1 1 # SPDX-License-Identifier: GPL-2.0
2 2  
3   -spinand-objs := core.o micron.o winbond.o
  3 +spinand-objs := core.o macronix.o micron.o winbond.o
4 4 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
drivers/mtd/nand/spi/core.c
... ... @@ -830,6 +830,7 @@
830 830 };
831 831  
832 832 static const struct spinand_manufacturer *spinand_manufacturers[] = {
  833 + &macronix_spinand_manufacturer,
833 834 &micron_spinand_manufacturer,
834 835 &winbond_spinand_manufacturer,
835 836 };
drivers/mtd/nand/spi/macronix.c
  1 +// SPDX-License-Identifier: GPL-2.0
  2 +/*
  3 + * Copyright (c) 2018 Macronix
  4 + *
  5 + * Author: Boris Brezillon <boris.brezillon@bootlin.com>
  6 + */
  7 +
  8 +#ifndef __UBOOT__
  9 +#include <linux/device.h>
  10 +#include <linux/kernel.h>
  11 +#endif
  12 +#include <linux/mtd/spinand.h>
  13 +
  14 +#define SPINAND_MFR_MACRONIX 0xC2
  15 +
  16 +static SPINAND_OP_VARIANTS(read_cache_variants,
  17 + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
  18 + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
  19 + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
  20 + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
  21 +
  22 +static SPINAND_OP_VARIANTS(write_cache_variants,
  23 + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
  24 + SPINAND_PROG_LOAD(true, 0, NULL, 0));
  25 +
  26 +static SPINAND_OP_VARIANTS(update_cache_variants,
  27 + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
  28 + SPINAND_PROG_LOAD(false, 0, NULL, 0));
  29 +
  30 +static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
  31 + struct mtd_oob_region *region)
  32 +{
  33 + return -ERANGE;
  34 +}
  35 +
  36 +static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section,
  37 + struct mtd_oob_region *region)
  38 +{
  39 + if (section)
  40 + return -ERANGE;
  41 +
  42 + region->offset = 2;
  43 + region->length = mtd->oobsize - 2;
  44 +
  45 + return 0;
  46 +}
  47 +
  48 +static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = {
  49 + .ecc = mx35lf1ge4ab_ooblayout_ecc,
  50 + .free = mx35lf1ge4ab_ooblayout_free,
  51 +};
  52 +
  53 +static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
  54 +{
  55 + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
  56 + SPI_MEM_OP_NO_ADDR,
  57 + SPI_MEM_OP_DUMMY(1, 1),
  58 + SPI_MEM_OP_DATA_IN(1, eccsr, 1));
  59 +
  60 + return spi_mem_exec_op(spinand->slave, &op);
  61 +}
  62 +
  63 +static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
  64 + u8 status)
  65 +{
  66 + struct nand_device *nand = spinand_to_nand(spinand);
  67 + u8 eccsr;
  68 +
  69 + switch (status & STATUS_ECC_MASK) {
  70 + case STATUS_ECC_NO_BITFLIPS:
  71 + return 0;
  72 +
  73 + case STATUS_ECC_UNCOR_ERROR:
  74 + return -EBADMSG;
  75 +
  76 + case STATUS_ECC_HAS_BITFLIPS:
  77 + /*
  78 + * Let's try to retrieve the real maximum number of bitflips
  79 + * in order to avoid forcing the wear-leveling layer to move
  80 + * data around if it's not necessary.
  81 + */
  82 + if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
  83 + return nand->eccreq.strength;
  84 +
  85 + if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr))
  86 + return nand->eccreq.strength;
  87 +
  88 + return eccsr;
  89 +
  90 + default:
  91 + break;
  92 + }
  93 +
  94 + return -EINVAL;
  95 +}
  96 +
  97 +static const struct spinand_info macronix_spinand_table[] = {
  98 + SPINAND_INFO("MX35LF1GE4AB", 0x12,
  99 + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
  100 + NAND_ECCREQ(4, 512),
  101 + SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
  102 + &write_cache_variants,
  103 + &update_cache_variants),
  104 + SPINAND_HAS_QE_BIT,
  105 + SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout,
  106 + mx35lf1ge4ab_ecc_get_status)),
  107 +};
  108 +
  109 +static int macronix_spinand_detect(struct spinand_device *spinand)
  110 +{
  111 + u8 *id = spinand->id.data;
  112 + int ret;
  113 +
  114 + /*
  115 + * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
  116 + * raw_id is garbage.
  117 + */
  118 + if (id[1] != SPINAND_MFR_MACRONIX)
  119 + return 0;
  120 +
  121 + ret = spinand_match_and_init(spinand, macronix_spinand_table,
  122 + ARRAY_SIZE(macronix_spinand_table),
  123 + id[2]);
  124 + if (ret)
  125 + return ret;
  126 +
  127 + return 1;
  128 +}
  129 +
  130 +static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
  131 + .detect = macronix_spinand_detect,
  132 +};
  133 +
  134 +const struct spinand_manufacturer macronix_spinand_manufacturer = {
  135 + .id = SPINAND_MFR_MACRONIX,
  136 + .name = "Macronix",
  137 + .ops = &macronix_spinand_manuf_ops,
  138 +};
include/linux/mtd/spinand.h
... ... @@ -201,6 +201,7 @@
201 201 };
202 202  
203 203 /* SPI NAND manufacturers */
  204 +extern const struct spinand_manufacturer macronix_spinand_manufacturer;
204 205 extern const struct spinand_manufacturer micron_spinand_manufacturer;
205 206 extern const struct spinand_manufacturer winbond_spinand_manufacturer;
206 207