diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-11 00:39:51 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-11 00:39:51 (GMT) |
commit | 35ff96dfd3c9aaa921b3e8dcac76b7697f2dcec0 (patch) | |
tree | 34e6ff93864142e629a1d179315039ae385a4c6e /drivers/mtd/nand/sunxi_nand.c | |
parent | 97d2116708ca0fd6ad8b00811ee4349b7e19e96f (diff) | |
parent | 69db4aa44fdd8befc2eccd1313d841c5128c385c (diff) | |
download | linux-35ff96dfd3c9aaa921b3e8dcac76b7697f2dcec0.tar.xz |
Merge tag 'for-linus-20161008' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris:
"I've not been very active this cycle, so these are mostly from Boris,
for the NAND flash subsystem.
NAND:
- Add the infrastructure to automate NAND timings configuration
- Provide a generic DT property to maximize ECC strength
- Some refactoring in the core bad block table handling, to help with
improving some of the logic in error cases.
- Minor cleanups and fixes
MTD:
- Add APIs for handling page pairing; this is necessary for reliably
supporting MLC and TLC NAND flash, where paired-page disturbance
affects reliability. Upper layers (e.g., UBI) should make use of
these in the near future"
* tag 'for-linus-20161008' of git://git.infradead.org/linux-mtd: (35 commits)
mtd: nand: fix trivial spelling error
mtdpart: Propagate _get/put_device()
mtd: nand: Provide nand_cleanup() function to free NAND related resources
mtd: Kill the OF_MTD Kconfig option
mtd: nand: mxc: Test CONFIG_OF instead of CONFIG_OF_MTD
mtd: nand: Fix nand_command_lp() for 8bits opcodes
mtd: nand: sunxi: Support ECC maximization
mtd: nand: Support maximizing ECC when using software BCH
mtd: nand: Add an option to maximize the ECC strength
mtd: nand: mxc: Add timing setup for v2 controllers
mtd: nand: mxc: implement onfi get/set features
mtd: nand: sunxi: switch from manual to automated timing config
mtd: nand: automate NAND timings selection
mtd: nand: Expose data interface for ONFI mode 0
mtd: nand: Add function to convert ONFI mode to data_interface
mtd: nand: convert ONFI mode into data interface
mtd: nand: Introduce nand_data_interface
mtd: nand: Create a NAND reset function
mtd: nand: remove unnecessary 'extern' from function declarations
MAINTAINERS: Add maintainer entry for Ingenic JZ4780 NAND driver
...
Diffstat (limited to 'drivers/mtd/nand/sunxi_nand.c')
-rw-r--r-- | drivers/mtd/nand/sunxi_nand.c | 108 |
1 files changed, 44 insertions, 64 deletions
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index e414b31..8b8470c 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1572,14 +1572,22 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, #define sunxi_nand_lookup_timing(l, p, c) \ _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) -static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, - const struct nand_sdr_timings *timings) +static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only) { + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *chip = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); + const struct nand_sdr_timings *timings; u32 min_clk_period = 0; s32 tWB, tADL, tWHR, tRHW, tCAD; long real_clk_rate; + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + /* T1 <=> tCLS */ if (timings->tCLS_min > min_clk_period) min_clk_period = timings->tCLS_min; @@ -1679,6 +1687,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, return tRHW; } + if (check_only) + return 0; + /* * TODO: according to ONFI specs this value only applies for DDR NAND, * but Allwinner seems to set this to 0x7. Mimic them for now. @@ -1712,44 +1723,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, return 0; } -static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip, - struct device_node *np) -{ - struct mtd_info *mtd = nand_to_mtd(&chip->nand); - const struct nand_sdr_timings *timings; - int ret; - int mode; - - mode = onfi_get_async_timing_mode(&chip->nand); - if (mode == ONFI_TIMING_MODE_UNKNOWN) { - mode = chip->nand.onfi_timing_mode_default; - } else { - uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; - int i; - - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - feature[0] = mode; - for (i = 0; i < chip->nsels; i++) { - chip->nand.select_chip(mtd, i); - ret = chip->nand.onfi_set_features(mtd, &chip->nand, - ONFI_FEATURE_ADDR_TIMING_MODE, - feature); - chip->nand.select_chip(mtd, -1); - if (ret) - return ret; - } - } - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - return sunxi_nand_chip_set_timings(chip, timings); -} - static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -1814,6 +1787,35 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, int ret; int i; + if (ecc->options & NAND_ECC_MAXIMIZE) { + int bytes; + + ecc->size = 1024; + nsectors = mtd->writesize / ecc->size; + + /* Reserve 2 bytes for the BBM */ + bytes = (mtd->oobsize - 2) / nsectors; + + /* 4 non-ECC bytes are added before each ECC bytes section */ + bytes -= 4; + + /* and bytes has to be even. */ + if (bytes % 2) + bytes--; + + ecc->strength = bytes * 8 / fls(8 * ecc->size); + + for (i = 0; i < ARRAY_SIZE(strengths); i++) { + if (strengths[i] > ecc->strength) + break; + } + + if (!i) + ecc->strength = 0; + else + ecc->strength = strengths[i - 1]; + } + if (ecc->size != 512 && ecc->size != 1024) return -EINVAL; @@ -1975,7 +1977,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, struct device_node *np) { - const struct nand_sdr_timings *timings; struct sunxi_nand_chip *chip; struct mtd_info *mtd; struct nand_chip *nand; @@ -2065,25 +2066,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, nand->read_buf = sunxi_nfc_read_buf; nand->write_buf = sunxi_nfc_write_buf; nand->read_byte = sunxi_nfc_read_byte; + nand->setup_data_interface = sunxi_nfc_setup_data_interface; mtd = nand_to_mtd(nand); mtd->dev.parent = dev; - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) { - ret = PTR_ERR(timings); - dev_err(dev, - "could not retrieve timings for ONFI mode 0: %d\n", - ret); - return ret; - } - - ret = sunxi_nand_chip_set_timings(chip, timings); - if (ret) { - dev_err(dev, "could not configure chip timings: %d\n", ret); - return ret; - } - ret = nand_scan_ident(mtd, nsels, NULL); if (ret) return ret; @@ -2096,12 +2083,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, nand->options |= NAND_SUBPAGE_READ; - ret = sunxi_nand_chip_init_timings(chip, np); - if (ret) { - dev_err(dev, "could not configure chip timings: %d\n", ret); - return ret; - } - ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); if (ret) { dev_err(dev, "ECC init failed: %d\n", ret); @@ -2175,8 +2156,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return -ENOMEM; nfc->dev = dev; - spin_lock_init(&nfc->controller.lock); - init_waitqueue_head(&nfc->controller.wq); + nand_hw_control_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |