xref: /linux/drivers/mtd/nand/spi/paragon.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
135526916SJeff Kletsky // SPDX-License-Identifier: GPL-2.0
235526916SJeff Kletsky /*
335526916SJeff Kletsky  * Copyright (C) 2019 Jeff Kletsky
435526916SJeff Kletsky  *
535526916SJeff Kletsky  * Author: Jeff Kletsky <git-commits@allycomm.com>
635526916SJeff Kletsky  */
735526916SJeff Kletsky 
835526916SJeff Kletsky #include <linux/device.h>
935526916SJeff Kletsky #include <linux/kernel.h>
1035526916SJeff Kletsky #include <linux/mtd/spinand.h>
1135526916SJeff Kletsky 
1235526916SJeff Kletsky 
1335526916SJeff Kletsky #define SPINAND_MFR_PARAGON	0xa1
1435526916SJeff Kletsky 
1535526916SJeff Kletsky 
1635526916SJeff Kletsky #define PN26G0XA_STATUS_ECC_BITMASK		(3 << 4)
1735526916SJeff Kletsky 
1835526916SJeff Kletsky #define PN26G0XA_STATUS_ECC_NONE_DETECTED	(0 << 4)
1935526916SJeff Kletsky #define PN26G0XA_STATUS_ECC_1_7_CORRECTED	(1 << 4)
2035526916SJeff Kletsky #define PN26G0XA_STATUS_ECC_ERRORED		(2 << 4)
2135526916SJeff Kletsky #define PN26G0XA_STATUS_ECC_8_CORRECTED		(3 << 4)
2235526916SJeff Kletsky 
2335526916SJeff Kletsky 
2435526916SJeff Kletsky static SPINAND_OP_VARIANTS(read_cache_variants,
2535526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
2635526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
2735526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
2835526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
2935526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
3035526916SJeff Kletsky 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
3135526916SJeff Kletsky 
3235526916SJeff Kletsky static SPINAND_OP_VARIANTS(write_cache_variants,
3335526916SJeff Kletsky 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
3435526916SJeff Kletsky 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
3535526916SJeff Kletsky 
3635526916SJeff Kletsky static SPINAND_OP_VARIANTS(update_cache_variants,
3735526916SJeff Kletsky 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
3835526916SJeff Kletsky 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
3935526916SJeff Kletsky 
4035526916SJeff Kletsky 
4135526916SJeff Kletsky static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
4235526916SJeff Kletsky 				   struct mtd_oob_region *region)
4335526916SJeff Kletsky {
4435526916SJeff Kletsky 	if (section > 3)
4535526916SJeff Kletsky 		return -ERANGE;
4635526916SJeff Kletsky 
4735526916SJeff Kletsky 	region->offset = 6 + (15 * section); /* 4 BBM + 2 user bytes */
4835526916SJeff Kletsky 	region->length = 13;
4935526916SJeff Kletsky 
5035526916SJeff Kletsky 	return 0;
5135526916SJeff Kletsky }
5235526916SJeff Kletsky 
5335526916SJeff Kletsky static int pn26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
5435526916SJeff Kletsky 				   struct mtd_oob_region *region)
5535526916SJeff Kletsky {
5635526916SJeff Kletsky 	if (section > 4)
5735526916SJeff Kletsky 		return -ERANGE;
5835526916SJeff Kletsky 
5935526916SJeff Kletsky 	if (section == 4) {
6035526916SJeff Kletsky 		region->offset = 64;
6135526916SJeff Kletsky 		region->length = 64;
6235526916SJeff Kletsky 	} else {
6335526916SJeff Kletsky 		region->offset = 4 + (15 * section);
6435526916SJeff Kletsky 		region->length = 2;
6535526916SJeff Kletsky 	}
6635526916SJeff Kletsky 
6735526916SJeff Kletsky 	return 0;
6835526916SJeff Kletsky }
6935526916SJeff Kletsky 
7035526916SJeff Kletsky static int pn26g0xa_ecc_get_status(struct spinand_device *spinand,
7135526916SJeff Kletsky 				   u8 status)
7235526916SJeff Kletsky {
7335526916SJeff Kletsky 	switch (status & PN26G0XA_STATUS_ECC_BITMASK) {
7435526916SJeff Kletsky 	case PN26G0XA_STATUS_ECC_NONE_DETECTED:
7535526916SJeff Kletsky 		return 0;
7635526916SJeff Kletsky 
7735526916SJeff Kletsky 	case PN26G0XA_STATUS_ECC_1_7_CORRECTED:
7835526916SJeff Kletsky 		return 7;	/* Return upper limit by convention */
7935526916SJeff Kletsky 
8035526916SJeff Kletsky 	case PN26G0XA_STATUS_ECC_8_CORRECTED:
8135526916SJeff Kletsky 		return 8;
8235526916SJeff Kletsky 
8335526916SJeff Kletsky 	case PN26G0XA_STATUS_ECC_ERRORED:
8435526916SJeff Kletsky 		return -EBADMSG;
8535526916SJeff Kletsky 
8635526916SJeff Kletsky 	default:
8735526916SJeff Kletsky 		break;
8835526916SJeff Kletsky 	}
8935526916SJeff Kletsky 
9035526916SJeff Kletsky 	return -EINVAL;
9135526916SJeff Kletsky }
9235526916SJeff Kletsky 
9335526916SJeff Kletsky static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = {
9435526916SJeff Kletsky 	.ecc = pn26g0xa_ooblayout_ecc,
9535526916SJeff Kletsky 	.free = pn26g0xa_ooblayout_free,
9635526916SJeff Kletsky };
9735526916SJeff Kletsky 
9835526916SJeff Kletsky 
9935526916SJeff Kletsky static const struct spinand_info paragon_spinand_table[] = {
100*f1541773SChuanhong Guo 	SPINAND_INFO("PN26G01A",
101*f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1),
10235526916SJeff Kletsky 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1),
10335526916SJeff Kletsky 		     NAND_ECCREQ(8, 512),
10435526916SJeff Kletsky 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
10535526916SJeff Kletsky 					      &write_cache_variants,
10635526916SJeff Kletsky 					      &update_cache_variants),
10735526916SJeff Kletsky 		     0,
10835526916SJeff Kletsky 		     SPINAND_ECCINFO(&pn26g0xa_ooblayout,
10935526916SJeff Kletsky 				     pn26g0xa_ecc_get_status)),
110*f1541773SChuanhong Guo 	SPINAND_INFO("PN26G02A",
111*f1541773SChuanhong Guo 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2),
11235526916SJeff Kletsky 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1),
11335526916SJeff Kletsky 		     NAND_ECCREQ(8, 512),
11435526916SJeff Kletsky 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
11535526916SJeff Kletsky 					      &write_cache_variants,
11635526916SJeff Kletsky 					      &update_cache_variants),
11735526916SJeff Kletsky 		     0,
11835526916SJeff Kletsky 		     SPINAND_ECCINFO(&pn26g0xa_ooblayout,
11935526916SJeff Kletsky 				     pn26g0xa_ecc_get_status)),
12035526916SJeff Kletsky };
12135526916SJeff Kletsky 
12235526916SJeff Kletsky static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = {
12335526916SJeff Kletsky };
12435526916SJeff Kletsky 
12535526916SJeff Kletsky const struct spinand_manufacturer paragon_spinand_manufacturer = {
12635526916SJeff Kletsky 	.id = SPINAND_MFR_PARAGON,
12735526916SJeff Kletsky 	.name = "Paragon",
128*f1541773SChuanhong Guo 	.chips = paragon_spinand_table,
129*f1541773SChuanhong Guo 	.nchips = ARRAY_SIZE(paragon_spinand_table),
13035526916SJeff Kletsky 	.ops = &paragon_spinand_manuf_ops,
13135526916SJeff Kletsky };
132