1118 lines
31 KiB
C
1118 lines
31 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#define pr_fmt(fmt) "sunxi-spinand-phy: " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mtd/aw-spinand.h>
|
|
#include <linux/of.h>
|
|
|
|
#include "physic.h"
|
|
|
|
#define KB (1024)
|
|
#define MB (KB * 1024)
|
|
#define to_kb(size) (size / KB)
|
|
#define to_mb(size) (size / MB)
|
|
|
|
|
|
/* manufacture num */
|
|
#define MICRON_MANUFACTURE 0x2c
|
|
#define GD_MANUFACTURE 0xc8
|
|
#define ATO_MANUFACTURE 0x9b
|
|
#define WINBOND_MANUFACTURE 0xef
|
|
#define MXIC_MANUFACTURE 0xc2
|
|
#define TOSHIBA_MANUFACTURE 0x98
|
|
#define ETRON_MANUFACTURE 0xd5
|
|
#define XTXTECH_MANUFACTURE 0x0b
|
|
#define DSTECH_MANUFACTURE 0xe5
|
|
#define FORESEE_MANUFACTURE 0xcd
|
|
#define ZETTA_MANUFACTURE 0xba
|
|
#define FM_MANUFACTURE 0xa1
|
|
#define SKYHIGH_MANUFACTURE 0x01
|
|
|
|
struct spinand_manufacture m;
|
|
|
|
struct aw_spinand_phy_info gigadevice[] = {
|
|
{
|
|
.Model = "GD5F1GQ4UCYIG",
|
|
.NandID = {0xc8, 0xb1, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ | SPINAND_ONEDUMMY_AFTER_RANDOMREAD,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT3_LIMIT2_TO_6_ERR7,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "GD5F1GQ4UBYIG",
|
|
.NandID = {0xc8, 0xd1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccFlag = HAS_EXT_ECC_SE01,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF4_LEN8_OFF4,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
/* GD5F2GQ4UB9IG did not check yet */
|
|
.Model = "GD5F2GQ4UB9IG",
|
|
.NandID = {0xc8, 0xd2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccFlag = HAS_EXT_ECC_SE01,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF4_LEN12,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "GD5F1GQ5UEYIG",
|
|
.NandID = {0xc8, 0x51, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccFlag = HAS_EXT_ECC_SE01,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF4_LEN12,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "F50L1G41LB(2M)",
|
|
.NandID = {0xc8, 0x01, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ | SPINAND_QUAD_NO_NEED_ENABLE,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
{
|
|
.Model = "GD5F2GM7UEYI",
|
|
.NandID = {0xc8, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccFlag = HAS_EXT_ECC_SE01,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info micron[] = {
|
|
{
|
|
.Model = "MT29F1G01ABAGDWB",
|
|
.NandID = {0x2c, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ | SPINAND_QUAD_NO_NEED_ENABLE,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT3_LIMIT5_ERR2,
|
|
.EccProtectedType = SIZE16_OFF32_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "MT29F2G01ABAGDWB",
|
|
.NandID = {0x2c, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ | SPINAND_QUAD_NO_NEED_ENABLE |
|
|
SPINAND_TWO_PLANE_SELECT,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT3_LIMIT5_ERR2,
|
|
.EccProtectedType = SIZE16_OFF32_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info xtx[] = {
|
|
{
|
|
/* XTX26G02A */
|
|
.Model = "XTX26G02A",
|
|
.NandID = {0x0B, 0xE2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.ecc_status_shift = ECC_STATUS_SHIFT_2,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF8_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
/* XTX26G02A */
|
|
.Model = "XTX26G01A",
|
|
.NandID = {0x0B, 0xE1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.ecc_status_shift = ECC_STATUS_SHIFT_2,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF8_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
/* XT26G01C */
|
|
.Model = "XT26G01C",
|
|
.NandID = {0x0B, 0x11, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.ecc_status_shift = ECC_STATUS_SHIFT_4,
|
|
.EccType = BIT4_LIMIT5_TO_8_ERR9_TO_15,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
/* XT26G02C */
|
|
.Model = "XT26G02CWSIG",
|
|
.NandID = {0x0B, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_DUAL_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.ecc_status_shift = ECC_STATUS_SHIFT_2,
|
|
.EccType = BIT4_LIMIT5_TO_7_ERR8_LIMIT_12,
|
|
.EccProtectedType = SIZE16_OFF8_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info fm[] = {
|
|
{
|
|
/* only rw stress test */
|
|
.Model = "FM25S01",
|
|
.NandID = {0xa1, 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ | SPINAND_QUAD_NO_NEED_ENABLE,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
{
|
|
.Model = "FM25S01A",
|
|
.NandID = {0xa1, 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info etron[] = {
|
|
|
|
};
|
|
|
|
struct aw_spinand_phy_info toshiba[] = {
|
|
|
|
};
|
|
|
|
struct aw_spinand_phy_info ato[] = {
|
|
|
|
};
|
|
|
|
struct aw_spinand_phy_info mxic[] = {
|
|
{
|
|
.Model = "MX35LF1GE4AB",
|
|
.NandID = {0xc2, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccFlag = HAS_EXT_ECC_STATUS,
|
|
.EccType = BIT4_LIMIT3_TO_4_ERR15,
|
|
/**
|
|
* MX35LF1GE4AB should use SIZE16_OFF4_LEN12, however, in order
|
|
* to compatibility with versions already sent to customers,
|
|
* which do not use general physical layout, we used
|
|
* SIZE16_OFF4_LEN4_OFF8 instead.
|
|
*/
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
{
|
|
.Model = "MX35LF2GE4AD",
|
|
.NandID = {0xc2, 0x26, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccFlag = HAS_EXT_ECC_STATUS,
|
|
.EccType = BIT4_LIMIT5_TO_8_ERR9_TO_15,
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info winbond[] = {
|
|
{
|
|
.Model = "W25N01GVZEIG",
|
|
.NandID = {0xef, 0xaa, 0x21, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "W25N02KV",
|
|
.NandID = {0xef, 0xaa, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_ERR2_LIMIT3,
|
|
.EccProtectedType = SIZE16_OFF4_LEN12,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "W25M02GV",
|
|
.NandID = {0xef, 0xab, 0x21, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_DUAL_READ |
|
|
SPINAND_QUAD_PROGRAM,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF4_LEN12,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info dosilicon[] = {
|
|
{
|
|
.Model = "DS35X1GAXXX",
|
|
.NandID = {0xe5, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 65000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info foresee[] = {
|
|
{
|
|
.Model = "FS35ND01G-S1F1QWFI000",
|
|
.NandID = {0xcd, 0xb1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT3_LIMIT3_TO_4_ERR7,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "FS35ND01G-S1Y2QWFI000",
|
|
.NandID = {0xcd, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
{
|
|
.Model = "FS35SQA001G",
|
|
.NandID = {0xcd, 0x71, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT2_LIMIT1_ERR2_TO_ERR3,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
|
|
{
|
|
.Model = "F35SQA002G",
|
|
.NandID = {0xcd, 0x72, 0x72, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info zetta[] = {
|
|
{
|
|
.Model = "ZD35Q1GAIB",
|
|
.NandID = {0xba, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 1024,
|
|
.OobSizePerPage = 64,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 50000,
|
|
.EccType = BIT2_LIMIT1_ERR2,
|
|
.EccProtectedType = SIZE16_OFF4_LEN4_OFF8,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
};
|
|
|
|
struct aw_spinand_phy_info skyhigh[] = {
|
|
{
|
|
.Model = "S35ML02G3",
|
|
.NandID = {0x01, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
|
.DieCntPerChip = 1,
|
|
.SectCntPerPage = 4,
|
|
.PageCntPerBlk = 64,
|
|
.BlkCntPerDie = 2048,
|
|
.OobSizePerPage = 128,
|
|
.OperationOpt = SPINAND_QUAD_READ | SPINAND_QUAD_PROGRAM |
|
|
SPINAND_DUAL_READ,
|
|
.MaxEraseTimes = 80000,
|
|
.EccType = BIT2_LIMIT2_ERR3,
|
|
.EccProtectedType = SIZE16_OFF0_LEN16,
|
|
.BadBlockFlag = BAD_BLK_FLAG_FIRST_2_PAGE,
|
|
},
|
|
};
|
|
|
|
|
|
static const char *aw_spinand_info_model(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->Model;
|
|
}
|
|
|
|
static void aw_spinand_info_nandid(struct aw_spinand_chip *chip,
|
|
unsigned char *id, int cnt)
|
|
{
|
|
int i;
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
cnt = min(cnt, MAX_ID_LEN);
|
|
for (i = 0; i < cnt; i++)
|
|
id[i] = pinfo->NandID[i];
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_sector_size(struct aw_spinand_chip *chip)
|
|
{
|
|
return 1 << SECTOR_SHIFT;
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_phy_page_size(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->SectCntPerPage * aw_spinand_info_sector_size(chip);
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_page_size(struct aw_spinand_chip *chip)
|
|
{
|
|
#if IS_ENABLED(CONFIG_AW_SPINAND_SIMULATE_MULTIPLANE)
|
|
return aw_spinand_info_phy_page_size(chip) * 2;
|
|
#else
|
|
return aw_spinand_info_phy_page_size(chip);
|
|
#endif
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_phy_block_size(
|
|
struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->PageCntPerBlk * aw_spinand_info_phy_page_size(chip);
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_block_size(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->PageCntPerBlk * aw_spinand_info_page_size(chip);
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_phy_oob_size(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->OobSizePerPage;
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_oob_size(struct aw_spinand_chip *chip)
|
|
{
|
|
#if IS_ENABLED(CONFIG_AW_SPINAND_SIMULATE_MULTIPLANE)
|
|
return aw_spinand_info_phy_oob_size(chip) * 2;
|
|
#else
|
|
return aw_spinand_info_phy_oob_size(chip);
|
|
#endif
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_die_cnt(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->DieCntPerChip;
|
|
}
|
|
|
|
static unsigned int aw_spinand_info_total_size(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->DieCntPerChip * pinfo->BlkCntPerDie *
|
|
aw_spinand_info_phy_block_size(chip);
|
|
}
|
|
|
|
static int aw_spinand_info_operation_opt(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->OperationOpt;
|
|
}
|
|
|
|
static int aw_spinand_info_max_erase_times(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo = chip->info->phy_info;
|
|
|
|
return pinfo->MaxEraseTimes;
|
|
}
|
|
|
|
struct spinand_manufacture {
|
|
unsigned char id;
|
|
const char *name;
|
|
struct aw_spinand_phy_info *info;
|
|
unsigned int cnt;
|
|
};
|
|
|
|
#define SPINAND_FACTORY_INFO(_id, _name, _info) \
|
|
{ \
|
|
.id = _id, \
|
|
.name = _name, \
|
|
.info = _info, \
|
|
.cnt = ARRAY_SIZE(_info), \
|
|
}
|
|
static struct spinand_manufacture spinand_factory[] = {
|
|
SPINAND_FACTORY_INFO(MICRON_MANUFACTURE, "Micron", micron),
|
|
SPINAND_FACTORY_INFO(GD_MANUFACTURE, "GD", gigadevice),
|
|
SPINAND_FACTORY_INFO(ATO_MANUFACTURE, "ATO", ato),
|
|
SPINAND_FACTORY_INFO(WINBOND_MANUFACTURE, "Winbond", winbond),
|
|
SPINAND_FACTORY_INFO(MXIC_MANUFACTURE, "Mxic", mxic),
|
|
SPINAND_FACTORY_INFO(TOSHIBA_MANUFACTURE, "Toshiba", toshiba),
|
|
SPINAND_FACTORY_INFO(ETRON_MANUFACTURE, "Etron", etron),
|
|
SPINAND_FACTORY_INFO(XTXTECH_MANUFACTURE, "XTX", xtx),
|
|
SPINAND_FACTORY_INFO(DSTECH_MANUFACTURE, "Dosilicon", dosilicon),
|
|
SPINAND_FACTORY_INFO(FORESEE_MANUFACTURE, "Foresee", foresee),
|
|
SPINAND_FACTORY_INFO(ZETTA_MANUFACTURE, "Zetta", zetta),
|
|
SPINAND_FACTORY_INFO(SKYHIGH_MANUFACTURE, "SkyHigh", skyhigh),
|
|
SPINAND_FACTORY_INFO(FM_MANUFACTURE, "FM", fm),
|
|
};
|
|
|
|
|
|
static int spinand_get_chip_munufacture(struct aw_spinand_chip *chip, const char **m)
|
|
{
|
|
struct aw_spinand_phy_info *info = chip->info->phy_info;
|
|
|
|
switch (info->NandID[0]) {
|
|
case MICRON_MANUFACTURE:
|
|
*m = "Micron";
|
|
break;
|
|
case GD_MANUFACTURE:
|
|
*m = "GD";
|
|
break;
|
|
case ATO_MANUFACTURE:
|
|
*m = "ATO";
|
|
break;
|
|
case WINBOND_MANUFACTURE:
|
|
*m = "Winbond";
|
|
break;
|
|
case MXIC_MANUFACTURE:
|
|
*m = "Mxic";
|
|
break;
|
|
case TOSHIBA_MANUFACTURE:
|
|
*m = "Toshiba";
|
|
break;
|
|
case ETRON_MANUFACTURE:
|
|
*m = "Etron";
|
|
break;
|
|
case XTXTECH_MANUFACTURE:
|
|
*m = "XTX";
|
|
break;
|
|
case DSTECH_MANUFACTURE:
|
|
*m = "Dosilicon";
|
|
break;
|
|
case FORESEE_MANUFACTURE:
|
|
*m = "Foresee";
|
|
break;
|
|
case ZETTA_MANUFACTURE:
|
|
*m = "Zetta";
|
|
break;
|
|
case SKYHIGH_MANUFACTURE:
|
|
*m = "SkyHigh";
|
|
break;
|
|
default:
|
|
*m = NULL;
|
|
break;
|
|
}
|
|
|
|
if (*m == NULL)
|
|
return false;
|
|
else
|
|
return true;
|
|
|
|
}
|
|
static const char *aw_spinand_info_manufacture(struct aw_spinand_chip *chip)
|
|
{
|
|
int i, j;
|
|
struct spinand_manufacture *m;
|
|
struct aw_spinand_phy_info *pinfo;
|
|
const char *m_name = NULL;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(spinand_factory); i++) {
|
|
m = &spinand_factory[i];
|
|
pinfo = chip->info->phy_info;
|
|
for (j = 0; j < m->cnt; j++)
|
|
if (pinfo == &m->info[j])
|
|
return m->name;
|
|
}
|
|
|
|
/*for compatible fdt support spi-nand*/
|
|
ret = spinand_get_chip_munufacture(chip, &m_name);
|
|
if (ret < 0)
|
|
return NULL;
|
|
else
|
|
return m_name;
|
|
}
|
|
|
|
static struct spinand_manufacture *spinand_detect_munufacture(unsigned char id)
|
|
{
|
|
int index;
|
|
struct spinand_manufacture *m;
|
|
|
|
for (index = 0; index < ARRAY_SIZE(spinand_factory); index++) {
|
|
m = &spinand_factory[index];
|
|
if (m->id == id) {
|
|
pr_info("detect munufacture from id table: %s\n", m->name);
|
|
return m;
|
|
}
|
|
}
|
|
|
|
pr_err("not detect any munufacture from id table\n");
|
|
return NULL;
|
|
}
|
|
|
|
static struct aw_spinand_phy_info *spinand_match_id(
|
|
struct spinand_manufacture *m,
|
|
unsigned char *id)
|
|
{
|
|
int i, j, match_max = 1, match_index = 0;
|
|
struct aw_spinand_phy_info *pinfo;
|
|
|
|
for (i = 0; i < m->cnt; i++) {
|
|
int match = 1;
|
|
|
|
pinfo = &m->info[i];
|
|
for (j = 1; j < MAX_ID_LEN; j++) {
|
|
/* 0xFF matching all ID value */
|
|
if (pinfo->NandID[j] != id[j] &&
|
|
pinfo->NandID[j] != 0xFF)
|
|
break;
|
|
|
|
if (pinfo->NandID[j] != 0xFF)
|
|
match++;
|
|
}
|
|
|
|
if (match > match_max) {
|
|
match_max = match;
|
|
match_index = i;
|
|
}
|
|
}
|
|
|
|
if (match_max > 1)
|
|
return &m->info[match_index];
|
|
return NULL;
|
|
}
|
|
|
|
struct aw_spinand_phy_info *spinand_get_phy_info_from_fdt(struct aw_spinand_chip *chip)
|
|
{
|
|
static struct aw_spinand_phy_info info;
|
|
static int had_get;
|
|
int ret = 0;
|
|
const char *bad_blk_mark_pos = NULL;
|
|
const char *quad_read_not_need_enable = NULL;
|
|
const char *read_seq_need_onedummy = NULL;
|
|
int len = 0;
|
|
u32 id = 0xffffffff;
|
|
struct device_node *node = chip->spi->dev.of_node;
|
|
u32 rx_bus_width = 0;
|
|
u32 tx_bus_width = 0;
|
|
|
|
|
|
if (had_get == true)
|
|
return &info;
|
|
|
|
#define BAD_BLK_MARK_POS1 "first_1_page"
|
|
#define BAD_BLK_MARK_POS2 "first_2_page"
|
|
#define BAD_BLK_MARK_POS3 "last_1_page"
|
|
#define BAD_BLK_MARK_POS4 "last_2_page"
|
|
memset(&info, 0, sizeof(struct aw_spinand_phy_info));
|
|
|
|
ret = of_property_read_string(node, "model", &(info.Model));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand Model from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "id-0", &id);
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand id Low 4 Byte from fdt fail\n");
|
|
goto err;
|
|
}
|
|
len = sizeof(id);
|
|
memmove(info.NandID, &id, min(MAX_ID_LEN, len));
|
|
|
|
id = 0xffffffff;
|
|
ret = of_property_read_u32(node, "id-1", &id);
|
|
if (ret < 0) {
|
|
pr_info("can't get spi-nand id high 4 Byte from fdt, may be not need\n");
|
|
}
|
|
memmove(info.NandID + min(MAX_ID_LEN, len), &id, max(MAX_ID_LEN, len) - min(MAX_ID_LEN, len));
|
|
|
|
ret = of_property_read_u32(node, "die_cnt_per_chip", &(info.DieCntPerChip));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand DieCntPerChip from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "blk_cnt_per_die", &(info.BlkCntPerDie));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand BlkCntPerDie from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "page_cnt_per_blk", &(info.PageCntPerBlk));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand PageCntPerBlk from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "sect_cnt_per_page", &(info.SectCntPerPage));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand SectCntPerPage from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "oob_size_per_page", &(info.OobSizePerPage));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand OobSizePerPage from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_string(node, "bad_block_mark_pos", &bad_blk_mark_pos);
|
|
if (ret < 0 || NULL == bad_blk_mark_pos) {
|
|
pr_err("get spi-nand BadBlockFlag from fdt fail\n");
|
|
goto err;
|
|
} else {
|
|
if (!memcmp(bad_blk_mark_pos, BAD_BLK_MARK_POS1, strlen(BAD_BLK_MARK_POS1)))
|
|
info.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE;
|
|
else if (!memcmp(bad_blk_mark_pos, BAD_BLK_MARK_POS2, strlen(BAD_BLK_MARK_POS2)))
|
|
info.BadBlockFlag = BAD_BLK_FLAG_FRIST_1_PAGE;
|
|
else if (!memcmp(bad_blk_mark_pos, BAD_BLK_MARK_POS3, strlen(BAD_BLK_MARK_POS3)))
|
|
info.BadBlockFlag = BAD_BLK_FLAG_LAST_1_PAGE;
|
|
else if (!memcmp(bad_blk_mark_pos, BAD_BLK_MARK_POS4, strlen(BAD_BLK_MARK_POS4)))
|
|
info.BadBlockFlag = BAD_BLK_FLAG_LAST_2_PAGE;
|
|
else {
|
|
pr_err("get spi-nand BadBlockFlag pattern is not right\n");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_s32(node, "max_erase_times", &(info.MaxEraseTimes));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand MaxEraseTimes from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "ecc_type", &(info.EccType));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand EccFlag from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "ecc_protected_type", &(info.EccProtectedType));
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand ecc_protected_type from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "spi-rx-bus-width", &rx_bus_width);
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand spi-rx-bus-width from fdt fail\n");
|
|
goto err;
|
|
} else {
|
|
switch (rx_bus_width) {
|
|
case SPI_NBITS_DUAL:
|
|
info.OperationOpt |= SPINAND_DUAL_READ;
|
|
break;
|
|
case SPI_NBITS_QUAD:
|
|
info.OperationOpt |= SPINAND_QUAD_READ;
|
|
break;
|
|
default:
|
|
info.OperationOpt |= 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "spi-tx-bus-width", &tx_bus_width);
|
|
if (ret < 0) {
|
|
pr_err("get spi-nand spi-tx-bus-width from fdt fail\n");
|
|
goto err;
|
|
} else {
|
|
switch (tx_bus_width) {
|
|
case SPI_NBITS_QUAD:
|
|
info.OperationOpt |= SPINAND_QUAD_PROGRAM;
|
|
break;
|
|
default:
|
|
info.OperationOpt |= 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_string(node, "read_from_cache_x4_not_need_enable",
|
|
&quad_read_not_need_enable);
|
|
pr_info("%d quad_read_not_need_enable:%s\n", __LINE__, quad_read_not_need_enable);
|
|
if (ret < 0 || NULL == quad_read_not_need_enable) {
|
|
pr_info("can't get spi-nand read_from_cache_x4_need_enable or it is null,"
|
|
" maybe not need enable quad read before read from cache x4\n");
|
|
} else {
|
|
if (!memcmp(quad_read_not_need_enable, "yes", strlen("yes")))
|
|
info.OperationOpt |= SPINAND_QUAD_NO_NEED_ENABLE;
|
|
}
|
|
|
|
ret = of_property_read_string(node, "read_from_cache_need_onedummy",
|
|
&read_seq_need_onedummy);
|
|
if (ret < 0 || NULL == read_seq_need_onedummy) {
|
|
pr_info("can't get spi-nand read_from_cache_need_onedummy or it is null,"
|
|
" maybe read from cache sequence not need one dummy in second Byte\n");
|
|
} else {
|
|
if (!memcmp(read_seq_need_onedummy, "yes", strlen("yes")))
|
|
info.OperationOpt |= SPINAND_ONEDUMMY_AFTER_RANDOMREAD;
|
|
}
|
|
|
|
|
|
ret = of_property_read_s32(node, "ecc_flag", &(info.EccFlag));
|
|
if (ret < 0) {
|
|
pr_err("can't get spi-nand EccFlag from fdt,"
|
|
" maybe(default) use 0FH + C0H to get feature,wich obtain ecc status\n");
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "ecc_status_shift", &(info.ecc_status_shift));
|
|
if (ret < 0) {
|
|
pr_info("can't get spi-nand ecc_status_shift from fdt,"
|
|
" use default ecc_status_shift_4 to get ecc status in C0H\n");
|
|
}
|
|
|
|
pr_debug("get spinand phy info from fdt\n");
|
|
pr_debug("Model:%s\n", info.Model);
|
|
pr_debug("ID:%02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
info.NandID[0], info.NandID[1], info.NandID[2], info.NandID[3],
|
|
info.NandID[4], info.NandID[5], info.NandID[6], info.NandID[7]);
|
|
pr_debug("DieCntPerChip:%d\n", info.DieCntPerChip);
|
|
pr_debug("BlkCntPerDie:%d\n", info.BlkCntPerDie);
|
|
pr_debug("PageCntPerBlk:%d\n", info.PageCntPerBlk);
|
|
pr_debug("SectCntPerPage:%d\n", info.SectCntPerPage);
|
|
pr_debug("OobSizePerPage:%d\n", info.OobSizePerPage);
|
|
pr_debug("BadBlockFlag:%d\n", info.BadBlockFlag);
|
|
pr_debug("OperationOpt:0x%x\n", info.OperationOpt);
|
|
pr_debug("MaxEraseTimes:%d\n", info.MaxEraseTimes);
|
|
pr_debug("EccFlag:%x\n", info.EccFlag);
|
|
pr_debug("ecc_status_shift:%x\n", info.ecc_status_shift);
|
|
pr_debug("EccType:%x\n", info.EccType);
|
|
pr_debug("EccProtectedType:%x\n", info.EccProtectedType);
|
|
|
|
had_get = true;
|
|
|
|
return &info;
|
|
err:
|
|
had_get = false;
|
|
return NULL;
|
|
}
|
|
|
|
static struct aw_spinand_info aw_spinand_info = {
|
|
.model = aw_spinand_info_model,
|
|
.manufacture = aw_spinand_info_manufacture,
|
|
.nandid = aw_spinand_info_nandid,
|
|
.die_cnt = aw_spinand_info_die_cnt,
|
|
.oob_size = aw_spinand_info_oob_size,
|
|
.page_size = aw_spinand_info_page_size,
|
|
.block_size = aw_spinand_info_block_size,
|
|
.phy_oob_size = aw_spinand_info_phy_oob_size,
|
|
.phy_page_size = aw_spinand_info_phy_page_size,
|
|
.phy_block_size = aw_spinand_info_phy_block_size,
|
|
.sector_size = aw_spinand_info_sector_size,
|
|
.total_size = aw_spinand_info_total_size,
|
|
.operation_opt = aw_spinand_info_operation_opt,
|
|
.max_erase_times = aw_spinand_info_max_erase_times,
|
|
};
|
|
|
|
static struct spinand_manufacture *spinand_detect_munufacture_from_fdt(struct aw_spinand_chip *chip, unsigned char id)
|
|
{
|
|
struct aw_spinand_phy_info *info = NULL;
|
|
struct spinand_manufacture *pm = &m;
|
|
int ret = 0;
|
|
|
|
info = spinand_get_phy_info_from_fdt(chip);
|
|
if (info == NULL) {
|
|
pr_err("get phy info from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
if (id == info->NandID[0]) {
|
|
pm->id = info->NandID[0];
|
|
pm->info = info;
|
|
chip->info = &aw_spinand_info;
|
|
chip->info->phy_info = info;
|
|
ret = spinand_get_chip_munufacture(chip, &(pm->name));
|
|
if (ret < 0)
|
|
goto err;
|
|
else
|
|
pr_info("detect munufacture from fdt: %s \n", pm->name);
|
|
} else {
|
|
goto err;
|
|
}
|
|
|
|
return pm;
|
|
err:
|
|
pr_info("not detect munufacture from fdt\n");
|
|
return NULL;
|
|
}
|
|
|
|
static struct aw_spinand_phy_info *spinand_match_id_from_fdt(struct aw_spinand_chip *chip,
|
|
struct spinand_manufacture *m,
|
|
unsigned char *id)
|
|
{
|
|
struct aw_spinand_phy_info *info = NULL;
|
|
int i = 0;
|
|
|
|
info = spinand_get_phy_info_from_fdt(chip);
|
|
if (info == NULL) {
|
|
pr_err("get phy info from fdt fail\n");
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ID_LEN; i++) {
|
|
/*0xff match all id value*/
|
|
if (id[i] != info->NandID[i] && info->NandID[i] != 0xff)
|
|
goto err;
|
|
}
|
|
|
|
return info;
|
|
|
|
err:
|
|
return NULL;
|
|
}
|
|
static int aw_spinand_info_init(struct aw_spinand_chip *chip,
|
|
struct aw_spinand_phy_info *pinfo)
|
|
{
|
|
chip->info = &aw_spinand_info;
|
|
chip->info->phy_info = pinfo;
|
|
|
|
pr_info("========== arch info ==========\n");
|
|
pr_info("Model: %s\n", pinfo->Model);
|
|
pr_info("Munufacture: %s\n", aw_spinand_info_manufacture(chip));
|
|
pr_info("DieCntPerChip: %u\n", pinfo->DieCntPerChip);
|
|
pr_info("BlkCntPerDie: %u\n", pinfo->BlkCntPerDie);
|
|
pr_info("PageCntPerBlk: %u\n", pinfo->PageCntPerBlk);
|
|
pr_info("SectCntPerPage: %u\n", pinfo->SectCntPerPage);
|
|
pr_info("OobSizePerPage: %u\n", pinfo->OobSizePerPage);
|
|
pr_info("BadBlockFlag: 0x%x\n", pinfo->BadBlockFlag);
|
|
pr_info("OperationOpt: 0x%x\n", pinfo->OperationOpt);
|
|
pr_info("MaxEraseTimes: %d\n", pinfo->MaxEraseTimes);
|
|
pr_info("EccFlag: 0x%x\n", pinfo->EccFlag);
|
|
pr_info("EccType: %d\n", pinfo->EccType);
|
|
pr_info("EccProtectedType: %d\n", pinfo->EccProtectedType);
|
|
pr_info("========================================\n");
|
|
pr_info("\n");
|
|
pr_info("========== physical info ==========\n");
|
|
pr_info("TotalSize: %u M\n", to_mb(aw_spinand_info_total_size(chip)));
|
|
pr_info("SectorSize: %u B\n", aw_spinand_info_sector_size(chip));
|
|
pr_info("PageSize: %u K\n", to_kb(aw_spinand_info_phy_page_size(chip)));
|
|
pr_info("BlockSize: %u K\n", to_kb(aw_spinand_info_phy_block_size(chip)));
|
|
pr_info("OOBSize: %u B\n", aw_spinand_info_phy_oob_size(chip));
|
|
pr_info("========================================\n");
|
|
pr_info("\n");
|
|
pr_info("========== logical info ==========\n");
|
|
pr_info("TotalSize: %u M\n", to_mb(aw_spinand_info_total_size(chip)));
|
|
pr_info("SectorSize: %u B\n", aw_spinand_info_sector_size(chip));
|
|
pr_info("PageSize: %u K\n", to_kb(aw_spinand_info_page_size(chip)));
|
|
pr_info("BlockSize: %u K\n", to_kb(aw_spinand_info_block_size(chip)));
|
|
pr_info("OOBSize: %u B\n", aw_spinand_info_oob_size(chip));
|
|
pr_info("========================================\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int aw_spinand_chip_detect(struct aw_spinand_chip *chip)
|
|
{
|
|
struct aw_spinand_phy_info *pinfo;
|
|
struct spinand_manufacture *m;
|
|
unsigned char id[MAX_ID_LEN] = {0xFF};
|
|
struct aw_spinand_chip_ops *ops = chip->ops;
|
|
int ret, dummy = 1;
|
|
|
|
retry:
|
|
/*first read with dummy/address@0x00*/
|
|
ret = ops->read_id(chip, id, MAX_ID_LEN, dummy);
|
|
if (ret) {
|
|
pr_err("read id failed : %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
m = spinand_detect_munufacture(id[0]);
|
|
if (!m)
|
|
goto detect_from_fdt;
|
|
|
|
pinfo = spinand_match_id(m, id);
|
|
if (pinfo)
|
|
goto detect;
|
|
|
|
detect_from_fdt:
|
|
m = spinand_detect_munufacture_from_fdt(chip, id[0]);
|
|
if (!m)
|
|
goto not_detect;
|
|
|
|
pinfo = spinand_match_id_from_fdt(chip, m, id);
|
|
if (pinfo)
|
|
goto detect;
|
|
|
|
not_detect:
|
|
/* retry without dummy/address@0x00 */
|
|
if (dummy) {
|
|
dummy = 0;
|
|
goto retry;
|
|
}
|
|
pr_info("not match spinand: %x %x\n",
|
|
*(__u32 *)id,
|
|
*((__u32 *)id + 1));
|
|
return -ENODEV;
|
|
detect:
|
|
pr_info("detect spinand id: %x %x\n",
|
|
*((__u32 *)pinfo->NandID),
|
|
*((__u32 *)pinfo->NandID + 1));
|
|
return aw_spinand_info_init(chip, pinfo);
|
|
}
|
|
|
|
MODULE_AUTHOR("liaoweixiong <liaoweixiong@allwinnertech.com>");
|
|
MODULE_DESCRIPTION("Commond physic layer for Allwinner's spinand driver");
|