diff options
author | David S. Miller <davem@davemloft.net> | 2017-07-21 03:38:43 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-07-21 03:38:43 +0100 |
commit | 7a68ada6ec7d88c68057d3a4c2a517eb94289976 (patch) | |
tree | 51cd586e74fc92bfbdf382fa1544a235d908b25c /drivers/mtd/nand/mtk_nand.c | |
parent | 760446f967678e14ee1b6464ee1bb8562f299fa6 (diff) | |
parent | 96080f697786e0a30006fcbcc5b53f350fcb3e9f (diff) | |
download | linux-7a68ada6ec7d88c68057d3a4c2a517eb94289976.tar.bz2 |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Diffstat (limited to 'drivers/mtd/nand/mtk_nand.c')
-rw-r--r-- | drivers/mtd/nand/mtk_nand.c | 279 |
1 files changed, 168 insertions, 111 deletions
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c index 6c517c682939..f7ae99464375 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/mtk_nand.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/iopoll.h> #include <linux/of.h> +#include <linux/of_device.h> #include "mtk_ecc.h" /* NAND controller register definition */ @@ -38,23 +39,6 @@ #define NFI_PAGEFMT (0x04) #define PAGEFMT_FDM_ECC_SHIFT (12) #define PAGEFMT_FDM_SHIFT (8) -#define PAGEFMT_SPARE_16 (0) -#define PAGEFMT_SPARE_26 (1) -#define PAGEFMT_SPARE_27 (2) -#define PAGEFMT_SPARE_28 (3) -#define PAGEFMT_SPARE_32 (4) -#define PAGEFMT_SPARE_36 (5) -#define PAGEFMT_SPARE_40 (6) -#define PAGEFMT_SPARE_44 (7) -#define PAGEFMT_SPARE_48 (8) -#define PAGEFMT_SPARE_49 (9) -#define PAGEFMT_SPARE_50 (0xa) -#define PAGEFMT_SPARE_51 (0xb) -#define PAGEFMT_SPARE_52 (0xc) -#define PAGEFMT_SPARE_62 (0xd) -#define PAGEFMT_SPARE_63 (0xe) -#define PAGEFMT_SPARE_64 (0xf) -#define PAGEFMT_SPARE_SHIFT (4) #define PAGEFMT_SEC_SEL_512 BIT(2) #define PAGEFMT_512_2K (0) #define PAGEFMT_2K_4K (1) @@ -115,6 +99,17 @@ #define MTK_RESET_TIMEOUT (1000000) #define MTK_MAX_SECTOR (16) #define MTK_NAND_MAX_NSELS (2) +#define MTK_NFC_MIN_SPARE (16) +#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ + ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ + (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) + +struct mtk_nfc_caps { + const u8 *spare_size; + u8 num_spare_size; + u8 pageformat_spare_shift; + u8 nfi_clk_div; +}; struct mtk_nfc_bad_mark_ctl { void (*bm_swap)(struct mtd_info *, u8 *buf, int raw); @@ -155,6 +150,7 @@ struct mtk_nfc { struct mtk_ecc *ecc; struct device *dev; + const struct mtk_nfc_caps *caps; void __iomem *regs; struct completion done; @@ -163,6 +159,20 @@ struct mtk_nfc { u8 *buffer; }; +/* + * supported spare size of each IP. + * order should be the same with the spare size bitfiled defination of + * register NFI_PAGEFMT. + */ +static const u8 spare_size_mt2701[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64 +}; + +static const u8 spare_size_mt2712[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, + 74 +}; + static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand) { return container_of(nand, struct mtk_nfc_nand_chip, nand); @@ -308,7 +318,7 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); struct mtk_nfc *nfc = nand_get_controller_data(chip); - u32 fmt, spare; + u32 fmt, spare, i; if (!mtd->writesize) return 0; @@ -352,63 +362,21 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) if (chip->ecc.size == 1024) spare >>= 1; - switch (spare) { - case 16: - fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT); - break; - case 26: - fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT); - break; - case 27: - fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT); - break; - case 28: - fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT); - break; - case 32: - fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT); - break; - case 36: - fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT); - break; - case 40: - fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT); - break; - case 44: - fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT); - break; - case 48: - fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT); - break; - case 49: - fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT); - break; - case 50: - fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT); - break; - case 51: - fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT); - break; - case 52: - fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT); - break; - case 62: - fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT); - break; - case 63: - fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT); - break; - case 64: - fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT); - break; - default: - dev_err(nfc->dev, "invalid spare per sector %d\n", spare); + for (i = 0; i < nfc->caps->num_spare_size; i++) { + if (nfc->caps->spare_size[i] == spare) + break; + } + + if (i == nfc->caps->num_spare_size) { + dev_err(nfc->dev, "invalid spare size %d\n", spare); return -EINVAL; } + fmt |= i << nfc->caps->pageformat_spare_shift; + fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT; fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT; - nfi_writew(nfc, fmt, NFI_PAGEFMT); + nfi_writel(nfc, fmt, NFI_PAGEFMT); nfc->ecc_cfg.strength = chip->ecc.strength; nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size; @@ -531,6 +499,74 @@ static void mtk_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) mtk_nfc_write_byte(mtd, buf[i]); } +static int mtk_nfc_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); + const struct nand_sdr_timings *timings; + u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + rate = clk_get_rate(nfc->clk.nfi_clk); + /* There is a frequency divider in some IPs */ + rate /= nfc->caps->nfi_clk_div; + + /* turn clock rate into KHZ */ + rate /= 1000; + + tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; + tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); + tpoecs &= 0xf; + + tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; + tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); + tprecs &= 0x3f; + + /* sdr interface has no tCR which means CE# low to RE# low */ + tc2r = 0; + + tw2r = timings->tWHR_min / 1000; + tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); + tw2r = DIV_ROUND_UP(tw2r - 1, 2); + tw2r &= 0xf; + + twh = max(timings->tREH_min, timings->tWH_min) / 1000; + twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; + twh &= 0xf; + + twst = timings->tWP_min / 1000; + twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; + twst &= 0xf; + + trlt = max(timings->tREA_max, timings->tRP_min) / 1000; + trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; + trlt &= 0xf; + + /* + * ACCON: access timing control register + * ------------------------------------- + * 31:28: tpoecs, minimum required time for CS post pulling down after + * accessing the device + * 27:22: tprecs, minimum required time for CS pre pulling down before + * accessing the device + * 21:16: tc2r, minimum required time from NCEB low to NREB low + * 15:12: tw2r, minimum required time from NWEB high to NREB low. + * 11:08: twh, write enable hold time + * 07:04: twst, write wait states + * 03:00: trlt, read wait states + */ + trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); + nfi_writel(nfc, trlt, NFI_ACCCON); + + return 0; +} + static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data) { struct mtk_nfc *nfc = nand_get_controller_data(chip); @@ -988,28 +1024,13 @@ static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc) { /* - * ACCON: access timing control register - * ------------------------------------- - * 31:28: minimum required time for CS post pulling down after accessing - * the device - * 27:22: minimum required time for CS pre pulling down before accessing - * the device - * 21:16: minimum required time from NCEB low to NREB low - * 15:12: minimum required time from NWEB high to NREB low. - * 11:08: write enable hold time - * 07:04: write wait states - * 03:00: read wait states - */ - nfi_writel(nfc, 0x10804211, NFI_ACCCON); - - /* * CNRNB: nand ready/busy register * ------------------------------- * 7:4: timeout register for polling the NAND busy/ready signal * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. */ nfi_writew(nfc, 0xf1, NFI_CNRNB); - nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); + nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); mtk_nfc_hw_reset(nfc); @@ -1131,12 +1152,12 @@ static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl, } } -static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) +static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); - u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44, - 48, 49, 50, 51, 52, 62, 63, 64}; - u32 eccsteps, i; + struct mtk_nfc *nfc = nand_get_controller_data(nand); + const u8 *spare = nfc->caps->spare_size; + u32 eccsteps, i, closest_spare = 0; eccsteps = mtd->writesize / nand->ecc.size; *sps = mtd->oobsize / eccsteps; @@ -1144,28 +1165,31 @@ static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) if (nand->ecc.size == 1024) *sps >>= 1; - for (i = 0; i < ARRAY_SIZE(spare); i++) { - if (*sps <= spare[i]) { - if (!i) - *sps = spare[i]; - else if (*sps != spare[i]) - *sps = spare[i - 1]; - break; + if (*sps < MTK_NFC_MIN_SPARE) + return -EINVAL; + + for (i = 0; i < nfc->caps->num_spare_size; i++) { + if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) { + closest_spare = i; + if (*sps == spare[i]) + break; } } - if (i >= ARRAY_SIZE(spare)) - *sps = spare[ARRAY_SIZE(spare) - 1]; + *sps = spare[closest_spare]; if (nand->ecc.size == 1024) *sps <<= 1; + + return 0; } static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); + struct mtk_nfc *nfc = nand_get_controller_data(nand); u32 spare; - int free; + int free, ret; /* support only ecc hw mode */ if (nand->ecc.mode != NAND_ECC_HW) { @@ -1194,7 +1218,9 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) nand->ecc.size = 1024; } - mtk_nfc_set_spare_per_sector(&spare, mtd); + ret = mtk_nfc_set_spare_per_sector(&spare, mtd); + if (ret) + return ret; /* calculate oob bytes except ecc parity data */ free = ((nand->ecc.strength * ECC_PARITY_BITS) + 7) >> 3; @@ -1214,7 +1240,7 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) } } - mtk_ecc_adjust_strength(&nand->ecc.strength); + mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength); dev_info(dev, "eccsize %d eccstrength %d\n", nand->ecc.size, nand->ecc.strength); @@ -1271,6 +1297,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, nand->read_byte = mtk_nfc_read_byte; nand->read_buf = mtk_nfc_read_buf; nand->cmd_ctrl = mtk_nfc_cmd_ctrl; + nand->setup_data_interface = mtk_nfc_setup_data_interface; /* set default mode in case dt entry is missing */ nand->ecc.mode = NAND_ECC_HW; @@ -1312,7 +1339,10 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, return -EINVAL; } - mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); + ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); + if (ret) + return ret; + mtk_nfc_set_fdm(&chip->fdm, mtd); mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd); @@ -1354,12 +1384,39 @@ static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) return 0; } +static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = { + .spare_size = spare_size_mt2701, + .num_spare_size = 16, + .pageformat_spare_shift = 4, + .nfi_clk_div = 1, +}; + +static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = { + .spare_size = spare_size_mt2712, + .num_spare_size = 19, + .pageformat_spare_shift = 16, + .nfi_clk_div = 2, +}; + +static const struct of_device_id mtk_nfc_id_table[] = { + { + .compatible = "mediatek,mt2701-nfc", + .data = &mtk_nfc_caps_mt2701, + }, { + .compatible = "mediatek,mt2712-nfc", + .data = &mtk_nfc_caps_mt2712, + }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); + static int mtk_nfc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct mtk_nfc *nfc; struct resource *res; + const struct of_device_id *of_nfc_id = NULL; int ret, irq; nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); @@ -1423,6 +1480,14 @@ static int mtk_nfc_probe(struct platform_device *pdev) goto clk_disable; } + of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); + if (!of_nfc_id) { + ret = -ENODEV; + goto clk_disable; + } + + nfc->caps = of_nfc_id->data; + platform_set_drvdata(pdev, nfc); ret = mtk_nfc_nand_chips_init(dev, nfc); @@ -1485,8 +1550,6 @@ static int mtk_nfc_resume(struct device *dev) if (ret) return ret; - mtk_nfc_hw_init(nfc); - /* reset NAND chip if VCC was powered off */ list_for_each_entry(chip, &nfc->chips, node) { nand = &chip->nand; @@ -1503,12 +1566,6 @@ static int mtk_nfc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume); #endif -static const struct of_device_id mtk_nfc_id_table[] = { - { .compatible = "mediatek,mt2701-nfc" }, - {} -}; -MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); - static struct platform_driver mtk_nfc_driver = { .probe = mtk_nfc_probe, .remove = mtk_nfc_remove, |