Merge v4.5-rc1 with MTD -next development

To start from a good base.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
This commit is contained in:
Brian Norris 2016-01-24 20:34:07 -08:00
commit b33f7a437c
12 changed files with 364 additions and 81 deletions

View File

@ -66,11 +66,13 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
{
uint32_t buf;
size_t bytes_read;
int err;
if (mtd_read(master, offset, sizeof(buf), &bytes_read,
(uint8_t *)&buf) < 0) {
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
offset);
err = mtd_read(master, offset, sizeof(buf), &bytes_read,
(uint8_t *)&buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
goto out_default;
}
@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
int trx_part = -1;
int last_trx_part = -1;
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
int err;
/*
* Some really old flashes (like AT45DB*) had smaller erasesize-s, but
@ -118,8 +121,8 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* Parse block by block looking for magics */
for (offset = 0; offset <= master->size - blocksize;
offset += blocksize) {
/* Nothing more in higher memory */
if (offset >= 0x2000000)
/* Nothing more in higher memory on BCM47XX (MIPS) */
if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000)
break;
if (curr_part >= BCM47XXPART_MAX_PARTS) {
@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
}
/* Read beginning of the block */
if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
&bytes_read, (uint8_t *)buf) < 0) {
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
offset);
err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ,
&bytes_read, (uint8_t *)buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
continue;
}
@ -254,10 +258,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
}
/* Read middle of the block */
if (mtd_read(master, offset + 0x8000, 0x4,
&bytes_read, (uint8_t *)buf) < 0) {
pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
offset);
err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read,
(uint8_t *)buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
offset, err);
continue;
}
@ -277,10 +282,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
}
offset = master->size - possible_nvram_sizes[i];
if (mtd_read(master, offset, 0x4, &bytes_read,
(uint8_t *)buf) < 0) {
pr_err("mtd_read error while reading at offset 0x%X!\n",
offset);
err = mtd_read(master, offset, 0x4, &bytes_read,
(uint8_t *)buf);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while reading (offset 0x%X): %d\n",
offset, err);
continue;
}

View File

@ -825,7 +825,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
*(buf + byte_pos) ^= (1 << bit_pos);
pos = sector_num * host->pmecc_sector_size + byte_pos;
dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, *(buf + byte_pos));
} else {
/* Bit flip in OOB area */
@ -835,7 +835,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
ecc[tmp] ^= (1 << bit_pos);
pos = tmp + nand_chip->ecc.layout->eccpos[0];
dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, ecc[tmp]);
}
@ -1486,8 +1486,6 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
}
static const struct of_device_id atmel_nand_dt_ids[];
static int atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np)
{
@ -1498,7 +1496,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
enum of_gpio_flags flags = 0;
host->caps = (struct atmel_nand_caps *)
of_match_device(atmel_nand_dt_ids, host->dev)->data;
of_device_get_match_data(host->dev);
if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
if (val >= 32) {
@ -1550,7 +1548,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
(val != 24)) {
dev_err(host->dev,
"Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
"Required ECC strength not supported: %u\n",
val);
return -EINVAL;
}
@ -1560,7 +1558,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) {
if ((val != 512) && (val != 1024)) {
dev_err(host->dev,
"Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
"Required ECC sector size not supported: %u\n",
val);
return -EINVAL;
}

View File

@ -1,7 +1,7 @@
/*
* Freescale GPMI NAND Flash Driver
*
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
* Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
*
* This program is free software; you can redistribute it and/or modify
@ -136,7 +136,7 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
*
* We may have available oob space in this case.
*/
static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
@ -145,7 +145,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
unsigned int block_mark_bit_offset;
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
return false;
return -EINVAL;
switch (chip->ecc_step_ds) {
case SZ_512:
@ -158,19 +158,19 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
dev_err(this->dev,
"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
chip->ecc_strength_ds, chip->ecc_step_ds);
return false;
return -EINVAL;
}
geo->ecc_chunk_size = chip->ecc_step_ds;
geo->ecc_strength = round_up(chip->ecc_strength_ds, 2);
if (!gpmi_check_ecc(this))
return false;
return -EINVAL;
/* Keep the C >= O */
if (geo->ecc_chunk_size < mtd->oobsize) {
dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n",
chip->ecc_step_ds, mtd->oobsize);
return false;
return -EINVAL;
}
/* The default value, see comment in the legacy_set_geometry(). */
@ -242,7 +242,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
+ ALIGN(geo->ecc_chunk_count, 4);
if (!this->swap_block_mark)
return true;
return 0;
/* For bit swap. */
block_mark_bit_offset = mtd->writesize * 8 -
@ -251,7 +251,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
geo->block_mark_byte_offset = block_mark_bit_offset / 8;
geo->block_mark_bit_offset = block_mark_bit_offset % 8;
return true;
return 0;
}
static int legacy_set_geometry(struct gpmi_nand_data *this)
@ -285,7 +285,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
geo->ecc_strength = get_ecc_strength(this);
if (!gpmi_check_ecc(this)) {
dev_err(this->dev,
"required ecc strength of the NAND chip: %d is not supported by the GPMI controller (%d)\n",
"ecc strength: %d cannot be supported by the controller (%d)\n"
"try to use minimum ecc strength that NAND chip required\n",
geo->ecc_strength,
this->devdata->bch_max_ecc_strength);
return -EINVAL;
@ -366,10 +367,11 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
if (of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")
&& set_geometry_by_ecc_info(this))
return 0;
return legacy_set_geometry(this);
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
|| legacy_set_geometry(this))
return set_geometry_by_ecc_info(this);
return 0;
}
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
@ -2033,9 +2035,54 @@ static int gpmi_nand_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int gpmi_pm_suspend(struct device *dev)
{
struct gpmi_nand_data *this = dev_get_drvdata(dev);
release_dma_channels(this);
return 0;
}
static int gpmi_pm_resume(struct device *dev)
{
struct gpmi_nand_data *this = dev_get_drvdata(dev);
int ret;
ret = acquire_dma_channels(this);
if (ret < 0)
return ret;
/* re-init the GPMI registers */
this->flags &= ~GPMI_TIMING_INIT_OK;
ret = gpmi_init(this);
if (ret) {
dev_err(this->dev, "Error setting GPMI : %d\n", ret);
return ret;
}
/* re-init the BCH registers */
ret = bch_set_geometry(this);
if (ret) {
dev_err(this->dev, "Error setting BCH : %d\n", ret);
return ret;
}
/* re-init others */
gpmi_extra_init(this);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops gpmi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
};
static struct platform_driver gpmi_nand_driver = {
.driver = {
.name = "gpmi-nand",
.pm = &gpmi_pm_ops,
.of_match_table = gpmi_nand_id_table,
},
.probe = gpmi_nand_probe,

View File

@ -626,7 +626,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
static int mpc5121_nfc_probe(struct platform_device *op)
{
struct device_node *rootnode, *dn = op->dev.of_node;
struct device_node *dn = op->dev.of_node;
struct clk *clk;
struct device *dev = &op->dev;
struct mpc5121_nfc_prv *prv;
@ -712,18 +712,15 @@ static int mpc5121_nfc_probe(struct platform_device *op)
chip->ecc.mode = NAND_ECC_SOFT;
/* Support external chip-select logic on ADS5121 board */
rootnode = of_find_node_by_path("/");
if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) {
if (of_machine_is_compatible("fsl,mpc5121ads")) {
retval = ads5121_chipselect_init(mtd);
if (retval) {
dev_err(dev, "Chipselect init error!\n");
of_node_put(rootnode);
return retval;
}
chip->select_chip = ads5121_select_chip;
}
of_node_put(rootnode);
/* Enable NFC clock */
clk = devm_clk_get(dev, "ipg");

View File

@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
return ret;
}
EXPORT_SYMBOL(nand_scan_bbt);

View File

@ -50,8 +50,8 @@ struct nand_flash_dev nand_flash_ids[] = {
SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K),
4 },
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
NAND_ECC_INFO(40, SZ_1K), 4 },
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),

View File

@ -113,7 +113,7 @@ static int nuc900_check_rb(struct nuc900_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
val = __raw_readl(REG_SMISR);
val = __raw_readl(nand->reg + REG_SMISR);
val &= READYBUSY;
spin_unlock(&nand->lock);

View File

@ -60,6 +60,7 @@
#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
#define NFC_REG_SPARE_AREA 0x00A0
#define NFC_REG_PAT_ID 0x00A4
#define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800
@ -538,6 +539,174 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
}
/* These seed values have been extracted from Allwinner's BSP */
static const u16 sunxi_nfc_randomizer_page_seeds[] = {
0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
};
/*
* sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
* have been generated using
* sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
* the randomizer engine does internally before de/scrambling OOB data.
*
* Those tables are statically defined to avoid calculating randomizer state
* at runtime.
*/
static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
};
static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
};
static u16 sunxi_nfc_randomizer_step(u16 state, int count)
{
state &= 0x7fff;
/*
* This loop is just a simple implementation of a Fibonacci LFSR using
* the x16 + x15 + 1 polynomial.
*/
while (count--)
state = ((state >> 1) |
(((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
return state;
}
static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
{
const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
int mod = mtd_div_by_ws(mtd->erasesize, mtd);
if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
if (ecc) {
if (mtd->ecc_step_size == 512)
seeds = sunxi_nfc_randomizer_ecc512_seeds;
else
seeds = sunxi_nfc_randomizer_ecc1024_seeds;
}
return seeds[page % mod];
}
static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
int page, bool ecc)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
u16 state;
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
state = sunxi_nfc_randomizer_state(mtd, page, ecc);
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
{
u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
bbm[0] ^= state;
bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
}
static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len,
bool ecc, int page)
{
sunxi_nfc_randomizer_config(mtd, page, ecc);
sunxi_nfc_randomizer_enable(mtd);
sunxi_nfc_write_buf(mtd, buf, len);
sunxi_nfc_randomizer_disable(mtd);
}
static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
int len, bool ecc, int page)
{
sunxi_nfc_randomizer_config(mtd, page, ecc);
sunxi_nfc_randomizer_enable(mtd);
sunxi_nfc_read_buf(mtd, buf, len);
sunxi_nfc_randomizer_disable(mtd);
}
static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
@ -574,18 +743,20 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
u8 *data, int data_off,
u8 *oob, int oob_off,
int *cur_off,
unsigned int *max_bitflips)
unsigned int *max_bitflips,
bool bbm, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
u32 status;
int ret;
if (*cur_off != data_off)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
sunxi_nfc_read_buf(mtd, NULL, ecc->size);
sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
if (data_off + ecc->size != oob_off)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
@ -594,25 +765,54 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
if (ret)
return ret;
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
*cur_off = oob_off + ecc->bytes + 4;
status = readl(nfc->regs + NFC_REG_ECC_ST);
if (status & NFC_ECC_PAT_FOUND(0)) {
u8 pattern = 0xff;
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
pattern = 0x0;
memset(data, pattern, ecc->size);
memset(oob, pattern, ecc->bytes + 4);
return 1;
}
ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4);
sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
if (status & NFC_ECC_ERR(0)) {
/*
* Re-read the data with the randomizer disabled to identify
* bitflips in erased pages.
*/
if (nand->options & NAND_NEED_SCRAMBLING) {
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
nand->read_buf(mtd, data, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
}
ret = nand_check_erased_ecc_chunk(data, ecc->size,
oob, ecc->bytes + 4,
NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
} else {
/*
* The engine protects 4 bytes of OOB data per chunk.
@ -620,6 +820,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
*/
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
oob);
/* De-randomize the Bad Block Marker. */
if (bbm && nand->options & NAND_NEED_SCRAMBLING)
sunxi_nfc_randomize_bbm(mtd, page, oob);
}
if (ret < 0) {
@ -629,13 +833,12 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
}
*cur_off = oob_off + ecc->bytes + 4;
return 0;
return raw_mode;
}
static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off)
u8 *oob, int *cur_off,
bool randomize, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@ -649,7 +852,11 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
offset + mtd->writesize, -1);
sunxi_nfc_read_buf(mtd, oob + offset, len);
if (!randomize)
sunxi_nfc_read_buf(mtd, oob + offset, len);
else
sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
false, page);
*cur_off = mtd->oobsize + mtd->writesize;
}
@ -662,7 +869,8 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
const u8 *data, int data_off,
const u8 *oob, int oob_off,
int *cur_off)
int *cur_off, bool bbm,
int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
@ -672,11 +880,20 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (data_off != *cur_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
sunxi_nfc_write_buf(mtd, data, ecc->size);
sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
/* Fill OOB data in */
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(0));
if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
u8 user_data[4];
memcpy(user_data, oob, 4);
sunxi_nfc_randomize_bbm(mtd, page, user_data);
writel(sunxi_nfc_buf_to_user_data(user_data),
nfc->regs + NFC_REG_USER_DATA(0));
} else {
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(0));
}
if (data_off + ecc->size != oob_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
@ -685,11 +902,13 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
if (ret)
return ret;
sunxi_nfc_randomizer_enable(mtd);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
NFC_ACCESS_DIR | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
@ -699,7 +918,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
}
static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off)
u8 *oob, int *cur_off,
int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@ -713,7 +933,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
nand->cmdfunc(mtd, NAND_CMD_RNDIN,
offset + mtd->writesize, -1);
sunxi_nfc_write_buf(mtd, oob + offset, len);
sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
*cur_off = mtd->oobsize + mtd->writesize;
}
@ -725,6 +945,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0;
int ret, i, cur_off = 0;
bool raw_mode = false;
sunxi_nfc_hw_ecc_enable(mtd);
@ -736,13 +957,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips);
if (ret)
&cur_off, &max_bitflips,
!i, page);
if (ret < 0)
return ret;
else if (ret)
raw_mode = true;
}
if (oob_required)
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off);
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
!raw_mode, page);
sunxi_nfc_hw_ecc_disable(mtd);
@ -767,13 +992,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off);
&cur_off, !i, page);
if (ret)
return ret;
}
if (oob_required)
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
&cur_off, page);
sunxi_nfc_hw_ecc_disable(mtd);
@ -788,6 +1014,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0;
int ret, i, cur_off = 0;
bool raw_mode = false;
sunxi_nfc_hw_ecc_enable(mtd);
@ -799,13 +1026,16 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off, &cur_off,
&max_bitflips);
if (ret)
&max_bitflips, !i, page);
if (ret < 0)
return ret;
else if (ret)
raw_mode = true;
}
if (oob_required)
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off);
sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
!raw_mode, page);
sunxi_nfc_hw_ecc_disable(mtd);
@ -829,13 +1059,15 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
oob, oob_off, &cur_off);
oob, oob_off, &cur_off,
false, page);
if (ret)
return ret;
}
if (oob_required)
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off);
if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
&cur_off, page);
sunxi_nfc_hw_ecc_disable(mtd);
@ -1345,6 +1577,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (nand->bbt_options & NAND_BBT_USE_FLASH)
nand->bbt_options |= NAND_BBT_NO_OOB;
if (nand->options & NAND_NEED_SCRAMBLING)
nand->options |= NAND_NO_SUBPAGE_WRITE;
ret = sunxi_nand_chip_init_timings(chip, np);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);

View File

@ -179,7 +179,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
* by the onenand_release function.
*
*/
int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct onenand_chip *this = mtd->priv;
struct bbm_info *bbm = this->bbm;
@ -247,6 +247,3 @@ int onenand_default_bbt(struct mtd_info *mtd)
return onenand_scan_bbt(mtd, bbm->badblock_pattern);
}
EXPORT_SYMBOL(onenand_scan_bbt);
EXPORT_SYMBOL(onenand_default_bbt);

View File

@ -371,8 +371,8 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
return ret;
}
static int __init mtk_nor_init(struct mt8173_nor *mt8173_nor,
struct device_node *flash_node)
static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
struct device_node *flash_node)
{
int ret;
struct spi_nor *nor;

View File

@ -166,7 +166,6 @@ struct bbm_info {
};
/* OneNAND BBT interface */
extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int onenand_default_bbt(struct mtd_info *mtd);
#endif /* __LINUX_MTD_BBM_H */

View File

@ -168,6 +168,12 @@ typedef enum {
/* Device supports subpage reads */
#define NAND_SUBPAGE_READ 0x00001000
/*
* Some MLC NANDs need data scrambling to limit bitflips caused by repeated
* patterns.
*/
#define NAND_NEED_SCRAMBLING 0x00002000
/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG