Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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#include <linux/device.h>
  9#include <linux/kernel.h>
 10#include <linux/mtd/spinand.h>
 11
 12#define SPINAND_MFR_MACRONIX		0xC2
 13#define MACRONIX_ECCSR_MASK		0x0F
 14
 15static SPINAND_OP_VARIANTS(read_cache_variants,
 16		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
 17		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
 18		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
 19		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
 20
 21static SPINAND_OP_VARIANTS(write_cache_variants,
 22		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
 23		SPINAND_PROG_LOAD(true, 0, NULL, 0));
 24
 25static SPINAND_OP_VARIANTS(update_cache_variants,
 26		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
 27		SPINAND_PROG_LOAD(false, 0, NULL, 0));
 28
 29static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
 30				      struct mtd_oob_region *region)
 31{
 32	return -ERANGE;
 33}
 34
 35static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section,
 36				       struct mtd_oob_region *region)
 37{
 38	if (section)
 39		return -ERANGE;
 40
 41	region->offset = 2;
 42	region->length = mtd->oobsize - 2;
 43
 44	return 0;
 45}
 46
 47static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = {
 48	.ecc = mx35lfxge4ab_ooblayout_ecc,
 49	.free = mx35lfxge4ab_ooblayout_free,
 50};
 51
 52static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
 53{
 54	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
 55					  SPI_MEM_OP_NO_ADDR,
 56					  SPI_MEM_OP_DUMMY(1, 1),
 57					  SPI_MEM_OP_DATA_IN(1, eccsr, 1));
 58
 59	int ret = spi_mem_exec_op(spinand->spimem, &op);
 60	if (ret)
 61		return ret;
 62
 63	*eccsr &= MACRONIX_ECCSR_MASK;
 64	return 0;
 65}
 66
 67static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
 68				       u8 status)
 69{
 70	struct nand_device *nand = spinand_to_nand(spinand);
 71	u8 eccsr;
 72
 73	switch (status & STATUS_ECC_MASK) {
 74	case STATUS_ECC_NO_BITFLIPS:
 75		return 0;
 76
 77	case STATUS_ECC_UNCOR_ERROR:
 78		return -EBADMSG;
 79
 80	case STATUS_ECC_HAS_BITFLIPS:
 81		/*
 82		 * Let's try to retrieve the real maximum number of bitflips
 83		 * in order to avoid forcing the wear-leveling layer to move
 84		 * data around if it's not necessary.
 85		 */
 86		if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
 87			return nand->eccreq.strength;
 88
 89		if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr))
 90			return nand->eccreq.strength;
 91
 92		return eccsr;
 93
 94	default:
 95		break;
 96	}
 97
 98	return -EINVAL;
 99}
100
101static const struct spinand_info macronix_spinand_table[] = {
102	SPINAND_INFO("MX35LF1GE4AB",
103		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
104		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
105		     NAND_ECCREQ(4, 512),
106		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
107					      &write_cache_variants,
108					      &update_cache_variants),
109		     SPINAND_HAS_QE_BIT,
110		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
111				     mx35lf1ge4ab_ecc_get_status)),
112	SPINAND_INFO("MX35LF2GE4AB",
113		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22),
114		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
115		     NAND_ECCREQ(4, 512),
116		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
117					      &write_cache_variants,
118					      &update_cache_variants),
119		     SPINAND_HAS_QE_BIT,
120		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
121};
122
123static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
124};
125
126const struct spinand_manufacturer macronix_spinand_manufacturer = {
127	.id = SPINAND_MFR_MACRONIX,
128	.name = "Macronix",
129	.chips = macronix_spinand_table,
130	.nchips = ARRAY_SIZE(macronix_spinand_table),
131	.ops = &macronix_spinand_manuf_ops,
132};