162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016-2017 Micron Technology, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Peter Pan <peterpandong@micron.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/mtd/spinand.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define SPINAND_MFR_MICRON 0x2c 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define MICRON_STATUS_ECC_MASK GENMASK(6, 4) 1662306a36Sopenharmony_ci#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4) 1762306a36Sopenharmony_ci#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) 1862306a36Sopenharmony_ci#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) 1962306a36Sopenharmony_ci#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MICRON_CFG_CR BIT(0) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * As per datasheet, die selection is done by the 6th bit of Die 2562306a36Sopenharmony_ci * Select Register (Address 0xD0). 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define MICRON_DIE_SELECT_REG 0xD0 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define MICRON_SELECT_DIE(x) ((x) << 6) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(quadio_read_cache_variants, 3262306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 3362306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 3462306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 3562306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 3662306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 3762306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(x4_write_cache_variants, 4062306a36Sopenharmony_ci SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 4162306a36Sopenharmony_ci SPINAND_PROG_LOAD(true, 0, NULL, 0)); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(x4_update_cache_variants, 4462306a36Sopenharmony_ci SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 4562306a36Sopenharmony_ci SPINAND_PROG_LOAD(false, 0, NULL, 0)); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Micron MT29F2G01AAAED Device */ 4862306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(x4_read_cache_variants, 4962306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 5062306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 5162306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 5262306a36Sopenharmony_ci SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(x1_write_cache_variants, 5562306a36Sopenharmony_ci SPINAND_PROG_LOAD(true, 0, NULL, 0)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic SPINAND_OP_VARIANTS(x1_update_cache_variants, 5862306a36Sopenharmony_ci SPINAND_PROG_LOAD(false, 0, NULL, 0)); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, 6162306a36Sopenharmony_ci struct mtd_oob_region *region) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci if (section) 6462306a36Sopenharmony_ci return -ERANGE; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci region->offset = mtd->oobsize / 2; 6762306a36Sopenharmony_ci region->length = mtd->oobsize / 2; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int micron_8_ooblayout_free(struct mtd_info *mtd, int section, 7362306a36Sopenharmony_ci struct mtd_oob_region *region) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if (section) 7662306a36Sopenharmony_ci return -ERANGE; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* Reserve 2 bytes for the BBM. */ 7962306a36Sopenharmony_ci region->offset = 2; 8062306a36Sopenharmony_ci region->length = (mtd->oobsize / 2) - 2; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_8_ooblayout = { 8662306a36Sopenharmony_ci .ecc = micron_8_ooblayout_ecc, 8762306a36Sopenharmony_ci .free = micron_8_ooblayout_free, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section, 9162306a36Sopenharmony_ci struct mtd_oob_region *region) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct spinand_device *spinand = mtd_to_spinand(mtd); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (section >= spinand->base.memorg.pagesize / 9662306a36Sopenharmony_ci mtd->ecc_step_size) 9762306a36Sopenharmony_ci return -ERANGE; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci region->offset = (section * 16) + 8; 10062306a36Sopenharmony_ci region->length = 8; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int micron_4_ooblayout_free(struct mtd_info *mtd, int section, 10662306a36Sopenharmony_ci struct mtd_oob_region *region) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct spinand_device *spinand = mtd_to_spinand(mtd); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (section >= spinand->base.memorg.pagesize / 11162306a36Sopenharmony_ci mtd->ecc_step_size) 11262306a36Sopenharmony_ci return -ERANGE; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (section) { 11562306a36Sopenharmony_ci region->offset = 16 * section; 11662306a36Sopenharmony_ci region->length = 8; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci /* section 0 has two bytes reserved for the BBM */ 11962306a36Sopenharmony_ci region->offset = 2; 12062306a36Sopenharmony_ci region->length = 6; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops micron_4_ooblayout = { 12762306a36Sopenharmony_ci .ecc = micron_4_ooblayout_ecc, 12862306a36Sopenharmony_ci .free = micron_4_ooblayout_free, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int micron_select_target(struct spinand_device *spinand, 13262306a36Sopenharmony_ci unsigned int target) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG, 13562306a36Sopenharmony_ci spinand->scratchbuf); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (target > 1) 13862306a36Sopenharmony_ci return -EINVAL; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci *spinand->scratchbuf = MICRON_SELECT_DIE(target); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return spi_mem_exec_op(spinand->spimem, &op); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int micron_8_ecc_get_status(struct spinand_device *spinand, 14662306a36Sopenharmony_ci u8 status) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci switch (status & MICRON_STATUS_ECC_MASK) { 14962306a36Sopenharmony_ci case STATUS_ECC_NO_BITFLIPS: 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci case STATUS_ECC_UNCOR_ERROR: 15362306a36Sopenharmony_ci return -EBADMSG; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci case MICRON_STATUS_ECC_1TO3_BITFLIPS: 15662306a36Sopenharmony_ci return 3; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci case MICRON_STATUS_ECC_4TO6_BITFLIPS: 15962306a36Sopenharmony_ci return 6; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci case MICRON_STATUS_ECC_7TO8_BITFLIPS: 16262306a36Sopenharmony_ci return 8; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci default: 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct spinand_info micron_spinand_table[] = { 17262306a36Sopenharmony_ci /* M79A 2Gb 3.3V */ 17362306a36Sopenharmony_ci SPINAND_INFO("MT29F2G01ABAGD", 17462306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), 17562306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), 17662306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 17762306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 17862306a36Sopenharmony_ci &x4_write_cache_variants, 17962306a36Sopenharmony_ci &x4_update_cache_variants), 18062306a36Sopenharmony_ci 0, 18162306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 18262306a36Sopenharmony_ci micron_8_ecc_get_status)), 18362306a36Sopenharmony_ci /* M79A 2Gb 1.8V */ 18462306a36Sopenharmony_ci SPINAND_INFO("MT29F2G01ABBGD", 18562306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), 18662306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), 18762306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 18862306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 18962306a36Sopenharmony_ci &x4_write_cache_variants, 19062306a36Sopenharmony_ci &x4_update_cache_variants), 19162306a36Sopenharmony_ci 0, 19262306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 19362306a36Sopenharmony_ci micron_8_ecc_get_status)), 19462306a36Sopenharmony_ci /* M78A 1Gb 3.3V */ 19562306a36Sopenharmony_ci SPINAND_INFO("MT29F1G01ABAFD", 19662306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), 19762306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 19862306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 19962306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 20062306a36Sopenharmony_ci &x4_write_cache_variants, 20162306a36Sopenharmony_ci &x4_update_cache_variants), 20262306a36Sopenharmony_ci 0, 20362306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 20462306a36Sopenharmony_ci micron_8_ecc_get_status)), 20562306a36Sopenharmony_ci /* M78A 1Gb 1.8V */ 20662306a36Sopenharmony_ci SPINAND_INFO("MT29F1G01ABAFD", 20762306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), 20862306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), 20962306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 21062306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 21162306a36Sopenharmony_ci &x4_write_cache_variants, 21262306a36Sopenharmony_ci &x4_update_cache_variants), 21362306a36Sopenharmony_ci 0, 21462306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 21562306a36Sopenharmony_ci micron_8_ecc_get_status)), 21662306a36Sopenharmony_ci /* M79A 4Gb 3.3V */ 21762306a36Sopenharmony_ci SPINAND_INFO("MT29F4G01ADAGD", 21862306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), 21962306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), 22062306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 22162306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 22262306a36Sopenharmony_ci &x4_write_cache_variants, 22362306a36Sopenharmony_ci &x4_update_cache_variants), 22462306a36Sopenharmony_ci 0, 22562306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 22662306a36Sopenharmony_ci micron_8_ecc_get_status), 22762306a36Sopenharmony_ci SPINAND_SELECT_TARGET(micron_select_target)), 22862306a36Sopenharmony_ci /* M70A 4Gb 3.3V */ 22962306a36Sopenharmony_ci SPINAND_INFO("MT29F4G01ABAFD", 23062306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), 23162306a36Sopenharmony_ci NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), 23262306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 23362306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 23462306a36Sopenharmony_ci &x4_write_cache_variants, 23562306a36Sopenharmony_ci &x4_update_cache_variants), 23662306a36Sopenharmony_ci SPINAND_HAS_CR_FEAT_BIT, 23762306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 23862306a36Sopenharmony_ci micron_8_ecc_get_status)), 23962306a36Sopenharmony_ci /* M70A 4Gb 1.8V */ 24062306a36Sopenharmony_ci SPINAND_INFO("MT29F4G01ABBFD", 24162306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), 24262306a36Sopenharmony_ci NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), 24362306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 24462306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 24562306a36Sopenharmony_ci &x4_write_cache_variants, 24662306a36Sopenharmony_ci &x4_update_cache_variants), 24762306a36Sopenharmony_ci SPINAND_HAS_CR_FEAT_BIT, 24862306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 24962306a36Sopenharmony_ci micron_8_ecc_get_status)), 25062306a36Sopenharmony_ci /* M70A 8Gb 3.3V */ 25162306a36Sopenharmony_ci SPINAND_INFO("MT29F8G01ADAFD", 25262306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), 25362306a36Sopenharmony_ci NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), 25462306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 25562306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 25662306a36Sopenharmony_ci &x4_write_cache_variants, 25762306a36Sopenharmony_ci &x4_update_cache_variants), 25862306a36Sopenharmony_ci SPINAND_HAS_CR_FEAT_BIT, 25962306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 26062306a36Sopenharmony_ci micron_8_ecc_get_status), 26162306a36Sopenharmony_ci SPINAND_SELECT_TARGET(micron_select_target)), 26262306a36Sopenharmony_ci /* M70A 8Gb 1.8V */ 26362306a36Sopenharmony_ci SPINAND_INFO("MT29F8G01ADBFD", 26462306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), 26562306a36Sopenharmony_ci NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), 26662306a36Sopenharmony_ci NAND_ECCREQ(8, 512), 26762306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, 26862306a36Sopenharmony_ci &x4_write_cache_variants, 26962306a36Sopenharmony_ci &x4_update_cache_variants), 27062306a36Sopenharmony_ci SPINAND_HAS_CR_FEAT_BIT, 27162306a36Sopenharmony_ci SPINAND_ECCINFO(µn_8_ooblayout, 27262306a36Sopenharmony_ci micron_8_ecc_get_status), 27362306a36Sopenharmony_ci SPINAND_SELECT_TARGET(micron_select_target)), 27462306a36Sopenharmony_ci /* M69A 2Gb 3.3V */ 27562306a36Sopenharmony_ci SPINAND_INFO("MT29F2G01AAAED", 27662306a36Sopenharmony_ci SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F), 27762306a36Sopenharmony_ci NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1), 27862306a36Sopenharmony_ci NAND_ECCREQ(4, 512), 27962306a36Sopenharmony_ci SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants, 28062306a36Sopenharmony_ci &x1_write_cache_variants, 28162306a36Sopenharmony_ci &x1_update_cache_variants), 28262306a36Sopenharmony_ci 0, 28362306a36Sopenharmony_ci SPINAND_ECCINFO(µn_4_ooblayout, NULL)), 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int micron_spinand_init(struct spinand_device *spinand) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * M70A device series enable Continuous Read feature at Power-up, 29062306a36Sopenharmony_ci * which is not supported. Disable this bit to avoid any possible 29162306a36Sopenharmony_ci * failure. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT) 29462306a36Sopenharmony_ci return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { 30062306a36Sopenharmony_ci .init = micron_spinand_init, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciconst struct spinand_manufacturer micron_spinand_manufacturer = { 30462306a36Sopenharmony_ci .id = SPINAND_MFR_MICRON, 30562306a36Sopenharmony_ci .name = "Micron", 30662306a36Sopenharmony_ci .chips = micron_spinand_table, 30762306a36Sopenharmony_ci .nchips = ARRAY_SIZE(micron_spinand_table), 30862306a36Sopenharmony_ci .ops = µn_spinand_manuf_ops, 30962306a36Sopenharmony_ci}; 310