310 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2016-2017 Micron Technology, Inc.4 *5 * Authors:6 * Peter Pan <peterpandong@micron.com>7 */8 9#include <linux/device.h>10#include <linux/kernel.h>11#include <linux/mtd/spinand.h>12 13#define SPINAND_MFR_MICRON 0x2c14 15#define MICRON_STATUS_ECC_MASK GENMASK(6, 4)16#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4)17#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)18#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)19#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4)20 21#define MICRON_CFG_CR BIT(0)22 23/*24 * As per datasheet, die selection is done by the 6th bit of Die25 * Select Register (Address 0xD0).26 */27#define MICRON_DIE_SELECT_REG 0xD028 29#define MICRON_SELECT_DIE(x) ((x) << 6)30 31static SPINAND_OP_VARIANTS(quadio_read_cache_variants,32 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),33 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),34 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),35 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),36 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),37 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));38 39static SPINAND_OP_VARIANTS(x4_write_cache_variants,40 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),41 SPINAND_PROG_LOAD(true, 0, NULL, 0));42 43static SPINAND_OP_VARIANTS(x4_update_cache_variants,44 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),45 SPINAND_PROG_LOAD(false, 0, NULL, 0));46 47/* Micron MT29F2G01AAAED Device */48static SPINAND_OP_VARIANTS(x4_read_cache_variants,49 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),50 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),51 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),52 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));53 54static SPINAND_OP_VARIANTS(x1_write_cache_variants,55 SPINAND_PROG_LOAD(true, 0, NULL, 0));56 57static SPINAND_OP_VARIANTS(x1_update_cache_variants,58 SPINAND_PROG_LOAD(false, 0, NULL, 0));59 60static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section,61 struct mtd_oob_region *region)62{63 if (section)64 return -ERANGE;65 66 region->offset = mtd->oobsize / 2;67 region->length = mtd->oobsize / 2;68 69 return 0;70}71 72static int micron_8_ooblayout_free(struct mtd_info *mtd, int section,73 struct mtd_oob_region *region)74{75 if (section)76 return -ERANGE;77 78 /* Reserve 2 bytes for the BBM. */79 region->offset = 2;80 region->length = (mtd->oobsize / 2) - 2;81 82 return 0;83}84 85static const struct mtd_ooblayout_ops micron_8_ooblayout = {86 .ecc = micron_8_ooblayout_ecc,87 .free = micron_8_ooblayout_free,88};89 90static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section,91 struct mtd_oob_region *region)92{93 struct spinand_device *spinand = mtd_to_spinand(mtd);94 95 if (section >= spinand->base.memorg.pagesize /96 mtd->ecc_step_size)97 return -ERANGE;98 99 region->offset = (section * 16) + 8;100 region->length = 8;101 102 return 0;103}104 105static int micron_4_ooblayout_free(struct mtd_info *mtd, int section,106 struct mtd_oob_region *region)107{108 struct spinand_device *spinand = mtd_to_spinand(mtd);109 110 if (section >= spinand->base.memorg.pagesize /111 mtd->ecc_step_size)112 return -ERANGE;113 114 if (section) {115 region->offset = 16 * section;116 region->length = 8;117 } else {118 /* section 0 has two bytes reserved for the BBM */119 region->offset = 2;120 region->length = 6;121 }122 123 return 0;124}125 126static const struct mtd_ooblayout_ops micron_4_ooblayout = {127 .ecc = micron_4_ooblayout_ecc,128 .free = micron_4_ooblayout_free,129};130 131static int micron_select_target(struct spinand_device *spinand,132 unsigned int target)133{134 struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG,135 spinand->scratchbuf);136 137 if (target > 1)138 return -EINVAL;139 140 *spinand->scratchbuf = MICRON_SELECT_DIE(target);141 142 return spi_mem_exec_op(spinand->spimem, &op);143}144 145static int micron_8_ecc_get_status(struct spinand_device *spinand,146 u8 status)147{148 switch (status & MICRON_STATUS_ECC_MASK) {149 case STATUS_ECC_NO_BITFLIPS:150 return 0;151 152 case STATUS_ECC_UNCOR_ERROR:153 return -EBADMSG;154 155 case MICRON_STATUS_ECC_1TO3_BITFLIPS:156 return 3;157 158 case MICRON_STATUS_ECC_4TO6_BITFLIPS:159 return 6;160 161 case MICRON_STATUS_ECC_7TO8_BITFLIPS:162 return 8;163 164 default:165 break;166 }167 168 return -EINVAL;169}170 171static const struct spinand_info micron_spinand_table[] = {172 /* M79A 2Gb 3.3V */173 SPINAND_INFO("MT29F2G01ABAGD",174 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),175 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),176 NAND_ECCREQ(8, 512),177 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,178 &x4_write_cache_variants,179 &x4_update_cache_variants),180 0,181 SPINAND_ECCINFO(µn_8_ooblayout,182 micron_8_ecc_get_status)),183 /* M79A 2Gb 1.8V */184 SPINAND_INFO("MT29F2G01ABBGD",185 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),186 NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),187 NAND_ECCREQ(8, 512),188 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,189 &x4_write_cache_variants,190 &x4_update_cache_variants),191 0,192 SPINAND_ECCINFO(µn_8_ooblayout,193 micron_8_ecc_get_status)),194 /* M78A 1Gb 3.3V */195 SPINAND_INFO("MT29F1G01ABAFD",196 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),197 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),198 NAND_ECCREQ(8, 512),199 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,200 &x4_write_cache_variants,201 &x4_update_cache_variants),202 0,203 SPINAND_ECCINFO(µn_8_ooblayout,204 micron_8_ecc_get_status)),205 /* M78A 1Gb 1.8V */206 SPINAND_INFO("MT29F1G01ABAFD",207 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),208 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),209 NAND_ECCREQ(8, 512),210 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,211 &x4_write_cache_variants,212 &x4_update_cache_variants),213 0,214 SPINAND_ECCINFO(µn_8_ooblayout,215 micron_8_ecc_get_status)),216 /* M79A 4Gb 3.3V */217 SPINAND_INFO("MT29F4G01ADAGD",218 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36),219 NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2),220 NAND_ECCREQ(8, 512),221 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,222 &x4_write_cache_variants,223 &x4_update_cache_variants),224 0,225 SPINAND_ECCINFO(µn_8_ooblayout,226 micron_8_ecc_get_status),227 SPINAND_SELECT_TARGET(micron_select_target)),228 /* M70A 4Gb 3.3V */229 SPINAND_INFO("MT29F4G01ABAFD",230 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),231 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),232 NAND_ECCREQ(8, 512),233 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,234 &x4_write_cache_variants,235 &x4_update_cache_variants),236 SPINAND_HAS_CR_FEAT_BIT,237 SPINAND_ECCINFO(µn_8_ooblayout,238 micron_8_ecc_get_status)),239 /* M70A 4Gb 1.8V */240 SPINAND_INFO("MT29F4G01ABBFD",241 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),242 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),243 NAND_ECCREQ(8, 512),244 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,245 &x4_write_cache_variants,246 &x4_update_cache_variants),247 SPINAND_HAS_CR_FEAT_BIT,248 SPINAND_ECCINFO(µn_8_ooblayout,249 micron_8_ecc_get_status)),250 /* M70A 8Gb 3.3V */251 SPINAND_INFO("MT29F8G01ADAFD",252 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46),253 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),254 NAND_ECCREQ(8, 512),255 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,256 &x4_write_cache_variants,257 &x4_update_cache_variants),258 SPINAND_HAS_CR_FEAT_BIT,259 SPINAND_ECCINFO(µn_8_ooblayout,260 micron_8_ecc_get_status),261 SPINAND_SELECT_TARGET(micron_select_target)),262 /* M70A 8Gb 1.8V */263 SPINAND_INFO("MT29F8G01ADBFD",264 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47),265 NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2),266 NAND_ECCREQ(8, 512),267 SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,268 &x4_write_cache_variants,269 &x4_update_cache_variants),270 SPINAND_HAS_CR_FEAT_BIT,271 SPINAND_ECCINFO(µn_8_ooblayout,272 micron_8_ecc_get_status),273 SPINAND_SELECT_TARGET(micron_select_target)),274 /* M69A 2Gb 3.3V */275 SPINAND_INFO("MT29F2G01AAAED",276 SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F),277 NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1),278 NAND_ECCREQ(4, 512),279 SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants,280 &x1_write_cache_variants,281 &x1_update_cache_variants),282 0,283 SPINAND_ECCINFO(µn_4_ooblayout, NULL)),284};285 286static int micron_spinand_init(struct spinand_device *spinand)287{288 /*289 * M70A device series enable Continuous Read feature at Power-up,290 * which is not supported. Disable this bit to avoid any possible291 * failure.292 */293 if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT)294 return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0);295 296 return 0;297}298 299static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {300 .init = micron_spinand_init,301};302 303const struct spinand_manufacturer micron_spinand_manufacturer = {304 .id = SPINAND_MFR_MICRON,305 .name = "Micron",306 .chips = micron_spinand_table,307 .nchips = ARRAY_SIZE(micron_spinand_table),308 .ops = µn_spinand_manuf_ops,309};310