mirror of
https://github.com/LuckfoxTECH/luckfox-pico.git
synced 2026-01-19 09:52:31 +01:00
project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp: Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem. sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately. sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades. sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for fastboot. Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
This commit is contained in:
@@ -8,18 +8,78 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef BBT_DEBUG
|
||||
#define BBT_DBG pr_err
|
||||
#define bbt_dbg pr_err
|
||||
#else
|
||||
#define BBT_DBG(args...)
|
||||
#define bbt_dbg(args...)
|
||||
#endif
|
||||
|
||||
#define BBT_VERSION_INVALID (0xFFFFFFFFU)
|
||||
#define BBT_VERSION_BLOCK_ABNORMAL (BBT_VERSION_INVALID - 1)
|
||||
#define BBT_VERSION_MAX (BBT_VERSION_INVALID - 8)
|
||||
|
||||
struct nanddev_bbt_info {
|
||||
u8 pattern[4];
|
||||
unsigned int version;
|
||||
u32 hash;
|
||||
};
|
||||
|
||||
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
|
||||
|
||||
#if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
|
||||
static void bbt_dbg_hex(char *s, void *buf, u32 len)
|
||||
{
|
||||
print_hex_dump(KERN_WARNING, s, DUMP_PREFIX_OFFSET, 4, 4, buf, len, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static u32 js_hash(u8 *buf, u32 len)
|
||||
{
|
||||
u32 hash = 0x47C6A7E6;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
hash ^= ((hash << 5) + buf[i] + (hash >> 2));
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static bool bbt_check_hash(u8 *buf, u32 len, u32 hash_cmp)
|
||||
{
|
||||
u32 hash;
|
||||
|
||||
/* compatible with no-hash version */
|
||||
if (hash_cmp == 0 || hash_cmp == 0xFFFFFFFF)
|
||||
return 1;
|
||||
|
||||
hash = js_hash(buf, len);
|
||||
if (hash != hash_cmp)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 bbt_nand_isbad_bypass(struct nand_device *nand, u32 block)
|
||||
{
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct nand_pos pos;
|
||||
|
||||
nanddev_bbt_set_block_status(nand, block, NAND_BBT_BLOCK_STATUS_UNKNOWN);
|
||||
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
|
||||
|
||||
return nanddev_isbad(nand, &pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* nanddev_read_bbt() - Read the BBT (Bad Block Table)
|
||||
* @nand: NAND device
|
||||
* @block: bbt block address
|
||||
* @update: true - get version and overwrite bbt.cache with new version;
|
||||
* false - get bbt version only;
|
||||
*
|
||||
* Initialize the in-memory BBT.
|
||||
*
|
||||
* Return: 0 in case of success, a negative error code otherwise.
|
||||
*/
|
||||
static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
|
||||
{
|
||||
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||
@@ -30,7 +90,7 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
|
||||
u8 *data_buf, *oob_buf;
|
||||
struct nanddev_bbt_info *bbt_info;
|
||||
struct mtd_oob_ops ops;
|
||||
int bbt_page_num;
|
||||
u32 bbt_page_num;
|
||||
int ret = 0;
|
||||
unsigned int version = 0;
|
||||
|
||||
@@ -40,7 +100,7 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
|
||||
if (block >= nblocks)
|
||||
return -EINVAL;
|
||||
|
||||
/* Aligned to page size, and even pages is better */
|
||||
/* aligned to page size, and even pages is better */
|
||||
bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
|
||||
mtd->writesize - 1) >> (ffs(mtd->writesize) - 1);
|
||||
bbt_page_num = (bbt_page_num + 1) / 2 * 2;
|
||||
@@ -64,29 +124,72 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
|
||||
ops.ooblen = bbt_page_num * mtd->oobsize;
|
||||
ops.ooboffs = 0;
|
||||
|
||||
/* store one entry for each block */
|
||||
ret = mtd_read_oob(mtd, block * mtd->erasesize, &ops);
|
||||
if (ret && ret != -EUCLEAN) {
|
||||
pr_err("%s fail %d\n", __func__, ret);
|
||||
ret = -EIO;
|
||||
pr_err("read_bbt blk=%d fail=%d update=%d\n", block, ret, update);
|
||||
ret = 0;
|
||||
version = BBT_VERSION_BLOCK_ABNORMAL;
|
||||
goto out;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (oob_buf[0] != 0xff && !memcmp(bbt_pattern, bbt_info->pattern, 4))
|
||||
version = bbt_info->version;
|
||||
/* bad block or good block without bbt */
|
||||
if (memcmp(bbt_pattern, bbt_info->pattern, 4)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BBT_DBG("read_bbt from blk=%d tag=%d ver=%d\n", block, update, version);
|
||||
/* good block with abnornal bbt */
|
||||
if (oob_buf[0] == 0xff ||
|
||||
!bbt_check_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4, bbt_info->hash)) {
|
||||
pr_err("read_bbt check fail blk=%d ret=%d update=%d\n", block, ret, update);
|
||||
ret = 0;
|
||||
version = BBT_VERSION_BLOCK_ABNORMAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* good block with good bbt */
|
||||
version = bbt_info->version;
|
||||
bbt_dbg("read_bbt from blk=%d ver=%d update=%d\n", block, version, update);
|
||||
if (update && version > nand->bbt.version) {
|
||||
memcpy(nand->bbt.cache, data_buf, nbytes);
|
||||
nand->bbt.version = version;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(oob_buf);
|
||||
kfree(data_buf);
|
||||
#if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
|
||||
bbt_dbg_hex("bbt", data_buf, nbytes + sizeof(struct nanddev_bbt_info));
|
||||
if (version) {
|
||||
u8 *temp_buf = kzalloc(bbt_page_num * mtd->writesize, GFP_KERNEL);
|
||||
bool in_scan = nand->bbt.option & NANDDEV_BBT_SCANNED;
|
||||
|
||||
return ret < 0 ? -EIO : version;
|
||||
if (!temp_buf)
|
||||
goto out;
|
||||
|
||||
memcpy(temp_buf, nand->bbt.cache, nbytes);
|
||||
memcpy(nand->bbt.cache, data_buf, nbytes);
|
||||
|
||||
if (!in_scan)
|
||||
nand->bbt.option |= NANDDEV_BBT_SCANNED;
|
||||
for (block = 0; block < nblocks; block++) {
|
||||
ret = nanddev_bbt_get_block_status(nand, block);
|
||||
if (ret != NAND_BBT_BLOCK_GOOD)
|
||||
bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
|
||||
}
|
||||
if (!in_scan)
|
||||
nand->bbt.option &= ~NANDDEV_BBT_SCANNED;
|
||||
memcpy(nand->bbt.cache, temp_buf, nbytes);
|
||||
kfree(temp_buf);
|
||||
ret = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
kfree(data_buf);
|
||||
kfree(oob_buf);
|
||||
|
||||
return ret < 0 ? -EIO : (int)version;
|
||||
}
|
||||
|
||||
static int nanddev_write_bbt(struct nand_device *nand, u32 block)
|
||||
@@ -99,18 +202,18 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
|
||||
u8 *data_buf, *oob_buf;
|
||||
struct nanddev_bbt_info *bbt_info;
|
||||
struct mtd_oob_ops ops;
|
||||
int bbt_page_num;
|
||||
int ret = 0;
|
||||
u32 bbt_page_num;
|
||||
int ret = 0, version;
|
||||
struct nand_pos pos;
|
||||
|
||||
BBT_DBG("write_bbt to blk=%d ver=%d\n", block, nand->bbt.version);
|
||||
bbt_dbg("write_bbt to blk=%d ver=%d\n", block, nand->bbt.version);
|
||||
if (!nand->bbt.cache)
|
||||
return -ENOMEM;
|
||||
|
||||
if (block >= nblocks)
|
||||
return -EINVAL;
|
||||
|
||||
/* Aligned to page size, and even pages is better */
|
||||
/* aligned to page size, and even pages is better */
|
||||
bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
|
||||
mtd->writesize - 1) >> (ffs(mtd->writesize) - 1);
|
||||
bbt_page_num = (bbt_page_num + 1) / 2 * 2;
|
||||
@@ -130,7 +233,9 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
|
||||
memcpy(data_buf, nand->bbt.cache, nbytes);
|
||||
memcpy(bbt_info, bbt_pattern, 4);
|
||||
bbt_info->version = nand->bbt.version;
|
||||
bbt_info->hash = js_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4);
|
||||
|
||||
/* store one entry for each block */
|
||||
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
|
||||
ret = nand->ops->erase(nand, &pos);
|
||||
if (ret)
|
||||
@@ -144,10 +249,23 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
|
||||
ops.ooblen = bbt_page_num * mtd->oobsize;
|
||||
ops.ooboffs = 0;
|
||||
ret = mtd_write_oob(mtd, block * mtd->erasesize, &ops);
|
||||
if (ret) {
|
||||
nand->ops->erase(nand, &pos);
|
||||
goto out;
|
||||
}
|
||||
|
||||
version = nanddev_read_bbt(nand, block, false);
|
||||
if (version != bbt_info->version) {
|
||||
pr_err("bbt_write fail, blk=%d recheck fail %d-%d\n",
|
||||
block, version, bbt_info->version);
|
||||
nand->ops->erase(nand, &pos);
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
kfree(oob_buf);
|
||||
kfree(data_buf);
|
||||
kfree(oob_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -158,14 +276,30 @@ static int nanddev_bbt_format(struct nand_device *nand)
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
struct nand_pos pos;
|
||||
u32 start_block, block;
|
||||
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
|
||||
unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
|
||||
BITS_PER_LONG);
|
||||
|
||||
start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
|
||||
|
||||
for (block = 0; block < nblocks; block++) {
|
||||
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
|
||||
if (nanddev_isbad(nand, &pos))
|
||||
if (nanddev_isbad(nand, &pos)) {
|
||||
if (bbt_nand_isbad_bypass(nand, 0)) {
|
||||
memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
|
||||
pr_err("bbt_format fail, test good block %d fail\n", 0);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!bbt_nand_isbad_bypass(nand, block)) {
|
||||
memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
|
||||
pr_err("bbt_format fail, test bad block %d fail\n", block);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
nanddev_bbt_set_block_status(nand, block,
|
||||
NAND_BBT_BLOCK_FACTORY_BAD);
|
||||
}
|
||||
}
|
||||
|
||||
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
|
||||
@@ -197,16 +331,34 @@ int nanddev_scan_bbt_in_flash(struct nand_device *nand)
|
||||
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++)
|
||||
nanddev_read_bbt(nand, start_block + block, true);
|
||||
|
||||
nand->bbt.option |= NANDDEV_BBT_SCANNED;
|
||||
if (nand->bbt.version == 0) {
|
||||
nanddev_bbt_format(nand);
|
||||
ret = nanddev_bbt_format(nand);
|
||||
if (ret) {
|
||||
nand->bbt.option = 0;
|
||||
pr_err("%s format fail\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
ret = nanddev_bbt_in_flash_update(nand);
|
||||
if (ret) {
|
||||
nand->bbt.option = 0;
|
||||
pr_err("%s fail\n", __func__);
|
||||
pr_err("%s update fail\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
nand->bbt.option |= NANDDEV_BBT_SCANNED;
|
||||
#if defined(BBT_DEBUG)
|
||||
pr_err("scan_bbt success\n");
|
||||
if (nand->bbt.version) {
|
||||
for (block = 0; block < nblocks; block++) {
|
||||
ret = nanddev_bbt_get_block_status(nand, block);
|
||||
if (ret != NAND_BBT_BLOCK_GOOD)
|
||||
bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -222,31 +374,31 @@ EXPORT_SYMBOL_GPL(nanddev_scan_bbt_in_flash);
|
||||
*/
|
||||
int nanddev_bbt_in_flash_update(struct nand_device *nand)
|
||||
{
|
||||
struct nand_pos pos;
|
||||
struct mtd_info *mtd = nanddev_to_mtd(nand);
|
||||
|
||||
if (nand->bbt.option & NANDDEV_BBT_SCANNED) {
|
||||
unsigned int nblocks = nanddev_neraseblocks(nand);
|
||||
u32 bbt_version[NANDDEV_BBT_SCAN_MAXBLOCKS];
|
||||
int start_block, block;
|
||||
u32 min_version, block_des;
|
||||
int ret, count = 0;
|
||||
int ret, count = 0, status;
|
||||
|
||||
start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
|
||||
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
|
||||
ret = nanddev_bbt_get_block_status(nand, start_block + block);
|
||||
if (ret == NAND_BBT_BLOCK_FACTORY_BAD) {
|
||||
bbt_version[block] = 0xFFFFFFFF;
|
||||
continue;
|
||||
}
|
||||
ret = nanddev_read_bbt(nand, start_block + block,
|
||||
false);
|
||||
if (ret < 0)
|
||||
bbt_version[block] = 0xFFFFFFFF;
|
||||
else if (ret == 0)
|
||||
bbt_version[block] = 0;
|
||||
status = nanddev_bbt_get_block_status(nand, start_block + block);
|
||||
ret = nanddev_read_bbt(nand, start_block + block, false);
|
||||
if (ret == 0 && status == NAND_BBT_BLOCK_FACTORY_BAD)
|
||||
bbt_version[block] = BBT_VERSION_INVALID;
|
||||
else if (ret == -EIO)
|
||||
bbt_version[block] = BBT_VERSION_INVALID;
|
||||
else if (ret == BBT_VERSION_BLOCK_ABNORMAL)
|
||||
bbt_version[block] = ret;
|
||||
else
|
||||
bbt_version[block] = ret;
|
||||
}
|
||||
get_min_ver:
|
||||
min_version = 0xFFFFFFFF;
|
||||
min_version = BBT_VERSION_MAX;
|
||||
block_des = 0;
|
||||
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
|
||||
if (bbt_version[block] < min_version) {
|
||||
@@ -255,25 +407,38 @@ get_min_ver:
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite the BBT_VERSION_BLOCK_ABNORMAL block */
|
||||
if (nand->bbt.version < min_version)
|
||||
nand->bbt.version = min_version + 4;
|
||||
|
||||
if (block_des > 0) {
|
||||
nand->bbt.version++;
|
||||
ret = nanddev_write_bbt(nand, block_des);
|
||||
bbt_version[block_des - start_block] = 0xFFFFFFFF;
|
||||
if (ret) {
|
||||
pr_err("%s blk= %d ret= %d\n", __func__,
|
||||
block_des, ret);
|
||||
goto get_min_ver;
|
||||
} else {
|
||||
count++;
|
||||
if (count < 2)
|
||||
goto get_min_ver;
|
||||
BBT_DBG("%s success\n", __func__);
|
||||
}
|
||||
} else {
|
||||
pr_err("%s failed\n", __func__);
|
||||
pr_err("bbt_update fail, blk=%d ret= %d\n", block_des, ret);
|
||||
|
||||
return -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bbt_version[block_des - start_block] = BBT_VERSION_INVALID;
|
||||
count++;
|
||||
if (count < 2)
|
||||
goto get_min_ver;
|
||||
bbt_dbg("bbt_update success\n");
|
||||
} else {
|
||||
pr_err("bbt_update failed\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
|
||||
if (bbt_version[block] == BBT_VERSION_BLOCK_ABNORMAL) {
|
||||
block_des = start_block + block;
|
||||
nanddev_offs_to_pos(nand, block_des * mtd->erasesize, &pos);
|
||||
nand->ops->erase(nand, &pos);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
#define DATA_INTERFACE_REG 0x6C
|
||||
#define DIFACE_SDR_MODE(x) FIELD_PREP(GENMASK(2, 0), (x))
|
||||
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (X))
|
||||
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (x))
|
||||
#define DIFACE_SDR 0
|
||||
#define DIFACE_NVDDR BIT(9)
|
||||
|
||||
@@ -283,17 +283,17 @@ static int anfc_select_target(struct nand_chip *chip, int target)
|
||||
|
||||
/* Update clock frequency */
|
||||
if (nfc->cur_clk != anand->clk) {
|
||||
clk_disable_unprepare(nfc->controller_clk);
|
||||
ret = clk_set_rate(nfc->controller_clk, anand->clk);
|
||||
clk_disable_unprepare(nfc->bus_clk);
|
||||
ret = clk_set_rate(nfc->bus_clk, anand->clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Failed to change clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->controller_clk);
|
||||
ret = clk_prepare_enable(nfc->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev,
|
||||
"Failed to re-enable the controller clock\n");
|
||||
"Failed to re-enable the bus clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -884,21 +884,60 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
|
||||
struct anand *anand = to_anand(chip);
|
||||
struct arasan_nfc *nfc = to_anfc(chip->controller);
|
||||
struct device_node *np = nfc->dev->of_node;
|
||||
const struct nand_sdr_timings *sdr;
|
||||
const struct nand_nvddr_timings *nvddr;
|
||||
|
||||
if (nand_interface_is_nvddr(conf)) {
|
||||
nvddr = nand_get_nvddr_timings(conf);
|
||||
if (IS_ERR(nvddr))
|
||||
return PTR_ERR(nvddr);
|
||||
|
||||
/*
|
||||
* The controller only supports data payload requests which are
|
||||
* a multiple of 4. In practice, most data accesses are 4-byte
|
||||
* aligned and this is not an issue. However, rounding up will
|
||||
* simply be refused by the controller if we reached the end of
|
||||
* the device *and* we are using the NV-DDR interface(!). In
|
||||
* this situation, unaligned data requests ending at the device
|
||||
* boundary will confuse the controller and cannot be performed.
|
||||
*
|
||||
* This is something that happens in nand_read_subpage() when
|
||||
* selecting software ECC support and must be avoided.
|
||||
*/
|
||||
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT)
|
||||
return -ENOTSUPP;
|
||||
} else {
|
||||
sdr = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
}
|
||||
|
||||
if (target < 0)
|
||||
return 0;
|
||||
|
||||
anand->timings = DIFACE_SDR | DIFACE_SDR_MODE(conf->timings.mode);
|
||||
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
|
||||
if (nand_interface_is_sdr(conf))
|
||||
anand->timings = DIFACE_SDR |
|
||||
DIFACE_SDR_MODE(conf->timings.mode);
|
||||
else
|
||||
anand->timings = DIFACE_NVDDR |
|
||||
DIFACE_DDR_MODE(conf->timings.mode);
|
||||
|
||||
if (nand_interface_is_sdr(conf)) {
|
||||
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
|
||||
} else {
|
||||
/* ONFI timings are defined in picoseconds */
|
||||
anand->clk = div_u64((u64)NSEC_PER_SEC * 1000,
|
||||
conf->timings.nvddr.tCK_min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
|
||||
* with f > 90MHz (default clock is 100MHz) but signals are unstable
|
||||
* with higher modes. Hence we decrease a little bit the clock rate to
|
||||
* 80MHz when using modes 2-5 with this SoC.
|
||||
* 80MHz when using SDR modes 2-5 with this SoC.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-nand-controller") &&
|
||||
conf->timings.mode >= 2)
|
||||
nand_interface_is_sdr(conf) && conf->timings.mode >= 2)
|
||||
anand->clk = ANFC_XLNX_SDR_HS_CORE_CLK;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -405,6 +405,7 @@ static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
|
||||
|
||||
dma_async_issue_pending(nc->dmac);
|
||||
wait_for_completion(&finished);
|
||||
dma_unmap_single(nc->dev, buf_dma, len, dir);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1246,7 +1247,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
||||
nc = to_nand_controller(nand->base.controller);
|
||||
|
||||
/* DDR interface not supported. */
|
||||
if (conf->type != NAND_SDR_IFACE)
|
||||
if (!nand_interface_is_sdr(conf))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/*
|
||||
|
||||
@@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
|
||||
if (IS_ERR(cdns_ctrl->reg))
|
||||
return PTR_ERR(cdns_ctrl->reg);
|
||||
|
||||
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
|
||||
cdns_ctrl->io.dma = res->start;
|
||||
cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res);
|
||||
cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res);
|
||||
if (IS_ERR(cdns_ctrl->io.virt))
|
||||
return PTR_ERR(cdns_ctrl->io.virt);
|
||||
cdns_ctrl->io.dma = res->start;
|
||||
|
||||
dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk");
|
||||
if (IS_ERR(dt->clk))
|
||||
|
||||
@@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
denali->reg = ioremap(csr_base, csr_len);
|
||||
denali->reg = devm_ioremap(denali->dev, csr_base, csr_len);
|
||||
if (!denali->reg) {
|
||||
dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
denali->host = ioremap(mem_base, mem_len);
|
||||
denali->host = devm_ioremap(denali->dev, mem_base, mem_len);
|
||||
if (!denali->host) {
|
||||
dev_err(&dev->dev, "Spectra: ioremap failed!");
|
||||
ret = -ENOMEM;
|
||||
goto out_unmap_reg;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = denali_init(denali);
|
||||
if (ret)
|
||||
goto out_unmap_host;
|
||||
return ret;
|
||||
|
||||
nsels = denali->nbanks;
|
||||
|
||||
@@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
|
||||
out_remove_denali:
|
||||
denali_remove(denali);
|
||||
out_unmap_host:
|
||||
iounmap(denali->host);
|
||||
out_unmap_reg:
|
||||
iounmap(denali->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev)
|
||||
struct denali_controller *denali = pci_get_drvdata(dev);
|
||||
|
||||
denali_remove(denali);
|
||||
iounmap(denali->reg);
|
||||
iounmap(denali->host);
|
||||
}
|
||||
|
||||
static struct pci_driver denali_pci_driver = {
|
||||
|
||||
@@ -727,36 +727,40 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
|
||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
||||
unsigned int al;
|
||||
|
||||
switch (chip->ecc.engine_type) {
|
||||
/*
|
||||
* if ECC was not chosen in DT, decide whether to use HW or SW ECC from
|
||||
* CS Base Register
|
||||
*/
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) {
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
|
||||
BR_DECC_CHK_GEN) {
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
} else {
|
||||
/* otherwise fall back to default software ECC */
|
||||
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
||||
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
|
||||
}
|
||||
}
|
||||
|
||||
switch (chip->ecc.engine_type) {
|
||||
/* if HW ECC was chosen, setup ecc and oob layout */
|
||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||
chip->ecc.read_page = fsl_elbc_read_page;
|
||||
chip->ecc.write_page = fsl_elbc_write_page;
|
||||
chip->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
break;
|
||||
|
||||
/* if SW ECC was chosen in DT, we do not need to set anything here */
|
||||
/* if none or SW ECC was chosen, we do not need to set anything here */
|
||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||
case NAND_ECC_ENGINE_TYPE_SOFT:
|
||||
case NAND_ECC_ENGINE_TYPE_ON_DIE:
|
||||
break;
|
||||
|
||||
/* should we also implement *_ECC_ENGINE_CONTROLLER to do as above? */
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -653,8 +653,9 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||
unsigned int tRP_ps;
|
||||
bool use_half_period;
|
||||
int sample_delay_ps, sample_delay_factor;
|
||||
u16 busy_timeout_cycles;
|
||||
unsigned int busy_timeout_cycles;
|
||||
u8 wrn_dly_sel;
|
||||
u64 busy_timeout_ps;
|
||||
|
||||
if (sdr->tRC_min >= 30000) {
|
||||
/* ONFI non-EDO modes [0-3] */
|
||||
@@ -678,7 +679,8 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||
addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
|
||||
data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
|
||||
data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
|
||||
busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps);
|
||||
busy_timeout_ps = max(sdr->tBERS_max, sdr->tPROG_max);
|
||||
busy_timeout_cycles = TO_CYCLES(busy_timeout_ps, period_ps);
|
||||
|
||||
hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) |
|
||||
BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) |
|
||||
|
||||
@@ -2672,7 +2672,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
||||
chip->controller = &nfc->controller;
|
||||
nand_set_flash_node(chip, np);
|
||||
|
||||
if (!of_property_read_bool(np, "marvell,nand-keep-config"))
|
||||
if (of_property_read_bool(np, "marvell,nand-keep-config"))
|
||||
chip->options |= NAND_KEEP_TIMINGS;
|
||||
|
||||
mtd = nand_to_mtd(chip);
|
||||
|
||||
@@ -454,7 +454,7 @@ static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
|
||||
if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
|
||||
mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
|
||||
*bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
|
||||
*correct_bitmap |= 1 >> i;
|
||||
*correct_bitmap |= BIT_ULL(i);
|
||||
continue;
|
||||
}
|
||||
if ((nand->options & NAND_NEED_SCRAMBLING) &&
|
||||
@@ -800,7 +800,7 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
|
||||
u8 *data = buf + i * ecc->size;
|
||||
u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
|
||||
|
||||
if (correct_bitmap & (1 << i))
|
||||
if (correct_bitmap & BIT_ULL(i))
|
||||
continue;
|
||||
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||
oob, ecc->bytes + 2,
|
||||
@@ -1307,7 +1307,6 @@ static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
meson_nfc_free_buffer(&meson_chip->nand);
|
||||
nand_cleanup(&meson_chip->nand);
|
||||
list_del(&meson_chip->node);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
struct mtk_ecc_caps {
|
||||
u32 err_mask;
|
||||
u32 err_shift;
|
||||
const u8 *ecc_strength;
|
||||
const u32 *ecc_regs;
|
||||
u8 num_ecc_strength;
|
||||
@@ -76,7 +77,7 @@ static const u8 ecc_strength_mt2712[] = {
|
||||
};
|
||||
|
||||
static const u8 ecc_strength_mt7622[] = {
|
||||
4, 6, 8, 10, 12, 14, 16
|
||||
4, 6, 8, 10, 12
|
||||
};
|
||||
|
||||
enum mtk_ecc_regs {
|
||||
@@ -221,7 +222,7 @@ void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
|
||||
for (i = 0; i < sectors; i++) {
|
||||
offset = (i >> 2) << 2;
|
||||
err = readl(ecc->regs + ECC_DECENUM0 + offset);
|
||||
err = err >> ((i % 4) * 8);
|
||||
err = err >> ((i % 4) * ecc->caps->err_shift);
|
||||
err &= ecc->caps->err_mask;
|
||||
if (err == ecc->caps->err_mask) {
|
||||
/* uncorrectable errors */
|
||||
@@ -449,6 +450,7 @@ EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
|
||||
|
||||
static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
|
||||
.err_mask = 0x3f,
|
||||
.err_shift = 8,
|
||||
.ecc_strength = ecc_strength_mt2701,
|
||||
.ecc_regs = mt2701_ecc_regs,
|
||||
.num_ecc_strength = 20,
|
||||
@@ -459,6 +461,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
|
||||
|
||||
static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
|
||||
.err_mask = 0x7f,
|
||||
.err_shift = 8,
|
||||
.ecc_strength = ecc_strength_mt2712,
|
||||
.ecc_regs = mt2712_ecc_regs,
|
||||
.num_ecc_strength = 23,
|
||||
@@ -468,10 +471,11 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
|
||||
};
|
||||
|
||||
static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
|
||||
.err_mask = 0x3f,
|
||||
.err_mask = 0x1f,
|
||||
.err_shift = 5,
|
||||
.ecc_strength = ecc_strength_mt7622,
|
||||
.ecc_regs = mt7622_ecc_regs,
|
||||
.num_ecc_strength = 7,
|
||||
.num_ecc_strength = 5,
|
||||
.ecc_mode_shift = 4,
|
||||
.parity_bits = 13,
|
||||
.pg_irq_sel = 0,
|
||||
|
||||
@@ -292,6 +292,261 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct nand_interface_config onfi_nvddr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 0,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 10000,
|
||||
.tCALH_min = 10000,
|
||||
.tCALS_min = 10000,
|
||||
.tCAS_min = 10000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCK_min = 50000,
|
||||
.tCS_min = 35000,
|
||||
.tDH_min = 5000,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 5000,
|
||||
.tDS_min = 5000,
|
||||
.tDSC_min = 50000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 6000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 1 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 1,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 5000,
|
||||
.tCALH_min = 5000,
|
||||
.tCALS_min = 5000,
|
||||
.tCAS_min = 5000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCK_min = 30000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 2500,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 2500,
|
||||
.tDS_min = 3000,
|
||||
.tDSC_min = 30000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 3000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 2 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 2,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 4000,
|
||||
.tCALH_min = 4000,
|
||||
.tCALS_min = 4000,
|
||||
.tCAS_min = 4000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 4000,
|
||||
.tCK_min = 20000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1700,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1700,
|
||||
.tDS_min = 2000,
|
||||
.tDSC_min = 20000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 2000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 3 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 3,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 3000,
|
||||
.tCALH_min = 3000,
|
||||
.tCALS_min = 3000,
|
||||
.tCAS_min = 3000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 3000,
|
||||
.tCK_min = 15000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1300,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1300,
|
||||
.tDS_min = 1500,
|
||||
.tDSC_min = 15000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1500,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 4 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 4,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 2500,
|
||||
.tCALH_min = 2500,
|
||||
.tCALS_min = 2500,
|
||||
.tCAS_min = 2500,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 2500,
|
||||
.tCK_min = 12000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 1100,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 1000,
|
||||
.tDS_min = 1100,
|
||||
.tDSC_min = 12000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1200,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
/* Mode 5 */
|
||||
{
|
||||
.type = NAND_NVDDR_IFACE,
|
||||
.timings.mode = 5,
|
||||
.timings.nvddr = {
|
||||
.tCCS_min = 500000,
|
||||
.tR_max = 200000000,
|
||||
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
|
||||
.tAC_min = 3000,
|
||||
.tAC_max = 25000,
|
||||
.tADL_min = 400000,
|
||||
.tCAD_min = 45000,
|
||||
.tCAH_min = 2000,
|
||||
.tCALH_min = 2000,
|
||||
.tCALS_min = 2000,
|
||||
.tCAS_min = 2000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 2000,
|
||||
.tCK_min = 10000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 900,
|
||||
.tDQSCK_min = 3000,
|
||||
.tDQSCK_max = 25000,
|
||||
.tDQSD_min = 0,
|
||||
.tDQSD_max = 18000,
|
||||
.tDQSHZ_max = 20000,
|
||||
.tDQSQ_max = 850,
|
||||
.tDS_min = 900,
|
||||
.tDSC_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tITC_max = 1000000,
|
||||
.tQHS_max = 1000,
|
||||
.tRHW_min = 100000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWHR_min = 80000,
|
||||
.tWRCK_min = 20000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* All NAND chips share the same reset data interface: SDR mode 0 */
|
||||
const struct nand_interface_config *nand_get_reset_interface_config(void)
|
||||
{
|
||||
|
||||
@@ -384,7 +384,8 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
|
||||
dma_addr_t dma_addr;
|
||||
dma_cookie_t cookie;
|
||||
uint32_t reg;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
unsigned long time_left;
|
||||
|
||||
if (dir == DMA_FROM_DEVICE) {
|
||||
chan = flctl->chan_fifo0_rx;
|
||||
@@ -425,13 +426,14 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret =
|
||||
time_left =
|
||||
wait_for_completion_timeout(&flctl->dma_complete,
|
||||
msecs_to_jiffies(3000));
|
||||
|
||||
if (ret <= 0) {
|
||||
if (time_left == 0) {
|
||||
dmaengine_terminate_all(chan);
|
||||
dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -441,7 +443,7 @@ out:
|
||||
|
||||
dma_unmap_single(chan->device->dev, dma_addr, len, dir);
|
||||
|
||||
/* ret > 0 is success */
|
||||
/* ret == 0 is success */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -465,7 +467,7 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
|
||||
|
||||
/* initiate DMA transfer */
|
||||
if (flctl->chan_fifo0_rx && rlen >= 32 &&
|
||||
flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE) > 0)
|
||||
!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE))
|
||||
goto convert; /* DMA success */
|
||||
|
||||
/* do polling transfer */
|
||||
@@ -524,7 +526,7 @@ static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
|
||||
|
||||
/* initiate DMA transfer */
|
||||
if (flctl->chan_fifo0_tx && rlen >= 32 &&
|
||||
flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE) > 0)
|
||||
!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE))
|
||||
return; /* DMA success */
|
||||
|
||||
/* do polling transfer */
|
||||
|
||||
@@ -527,7 +527,7 @@ static int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req,
|
||||
bool ecc_enabled)
|
||||
{
|
||||
u8 status;
|
||||
u8 status = 8;
|
||||
int ret;
|
||||
|
||||
ret = spinand_load_page_op(spinand, req);
|
||||
@@ -535,6 +535,13 @@ static int spinand_read_page(struct spinand_device *spinand,
|
||||
return ret;
|
||||
|
||||
ret = spinand_wait(spinand, &status);
|
||||
/*
|
||||
* When there is data outside of OIP in the status, the status data is
|
||||
* inaccurate and needs to be reconfirmed
|
||||
*/
|
||||
if (spinand->id.data[0] == 0x01 && status && !ret)
|
||||
ret = spinand_wait(spinand, &status);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#define SPINAND_MFR_DOSILICON 0xE5
|
||||
|
||||
#define DOSICON_STATUS_ECC_MASK GENMASK(7, 4)
|
||||
#define DOSICON_STATUS_ECC_MASK GENMASK(6, 4)
|
||||
#define DOSICON_STATUS_ECC_NO_BITFLIPS (0 << 4)
|
||||
#define DOSICON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)
|
||||
#define DOSICON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)
|
||||
@@ -193,6 +193,26 @@ static const struct spinand_info dosilicon_spinand_table[] = {
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
|
||||
ds35xxgb_ecc_get_status)),
|
||||
SPINAND_INFO("DS35Q12B",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xF5),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 512, 10, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
|
||||
ds35xxgb_ecc_get_status)),
|
||||
SPINAND_INFO("DS35M12B",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA5),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 512, 10, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
|
||||
ds35xxgb_ecc_get_status)),
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {
|
||||
|
||||
@@ -70,12 +70,13 @@ static const struct mtd_ooblayout_ops js28u1gqscahg_ooblayout = {
|
||||
static int js28u1gqscahg_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
u8 eccsr = (status & GENMASK(6, 4)) >> 2;
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 eccsr = (status & GENMASK(6, 4)) >> 4;
|
||||
|
||||
if (eccsr <= 7)
|
||||
if (eccsr < 4)
|
||||
return eccsr;
|
||||
else if (eccsr == 12)
|
||||
return 8;
|
||||
else if (eccsr == 4)
|
||||
return nanddev_get_ecc_requirements(nand)->strength;
|
||||
else
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static int s35ml04g3_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
|
||||
@@ -69,12 +69,13 @@ static const struct mtd_ooblayout_ops tx25g01_ooblayout = {
|
||||
static int tx25g01_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
u8 eccsr = (status & GENMASK(6, 4)) >> 2;
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 eccsr = (status & GENMASK(6, 4)) >> 4;
|
||||
|
||||
if (eccsr <= 7)
|
||||
if (eccsr < 4)
|
||||
return eccsr;
|
||||
else if (eccsr == 12)
|
||||
return 8;
|
||||
else if (eccsr == 4)
|
||||
return nanddev_get_ecc_requirements(nand)->strength;
|
||||
else
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
@@ -209,6 +209,15 @@ static const struct spinand_info winbond_spinand_table[] = {
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25n02kv_ooblayout,
|
||||
w25n02kv_ecc_get_status)),
|
||||
SPINAND_INFO("W25N01JWZEIG",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBC),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
|
||||
};
|
||||
|
||||
static int winbond_spinand_init(struct spinand_device *spinand)
|
||||
|
||||
@@ -203,6 +203,28 @@ static int xt26g01c_ecc_get_status(struct spinand_device *spinand,
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
static int xt26g11c_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return nanddev_get_ecc_requirements(nand)->strength;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct spinand_info xtx_spinand_table[] = {
|
||||
SPINAND_INFO("XT26G01A",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
|
||||
@@ -293,7 +315,61 @@ static const struct spinand_info xtx_spinand_table[] = {
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01c_ooblayout,
|
||||
xt26g01c_ecc_get_status)),
|
||||
xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26Q02DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26Q01DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26Q04DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x53),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26G01DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01b_ooblayout, xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26G02DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01b_ooblayout, xt26g11c_ecc_get_status)),
|
||||
SPINAND_INFO("XT26G04DWSIGA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x33),
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
|
||||
|
||||
Reference in New Issue
Block a user