162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TXx9 NAND flash memory controller driver 462306a36Sopenharmony_ci * Based on RBTX49xx patch from CELF patch archive. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) Copyright TOSHIBA CORPORATION 2004-2007 762306a36Sopenharmony_ci * All Rights Reserved. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1662306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1762306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/platform_data/txx9/ndfmc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* TXX9 NDFMC Registers */ 2262306a36Sopenharmony_ci#define TXX9_NDFDTR 0x00 2362306a36Sopenharmony_ci#define TXX9_NDFMCR 0x04 2462306a36Sopenharmony_ci#define TXX9_NDFSR 0x08 2562306a36Sopenharmony_ci#define TXX9_NDFISR 0x0c 2662306a36Sopenharmony_ci#define TXX9_NDFIMR 0x10 2762306a36Sopenharmony_ci#define TXX9_NDFSPR 0x14 2862306a36Sopenharmony_ci#define TXX9_NDFRSTR 0x18 /* not TX4939 */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* NDFMCR : NDFMC Mode Control */ 3162306a36Sopenharmony_ci#define TXX9_NDFMCR_WE 0x80 3262306a36Sopenharmony_ci#define TXX9_NDFMCR_ECC_ALL 0x60 3362306a36Sopenharmony_ci#define TXX9_NDFMCR_ECC_RESET 0x60 3462306a36Sopenharmony_ci#define TXX9_NDFMCR_ECC_READ 0x40 3562306a36Sopenharmony_ci#define TXX9_NDFMCR_ECC_ON 0x20 3662306a36Sopenharmony_ci#define TXX9_NDFMCR_ECC_OFF 0x00 3762306a36Sopenharmony_ci#define TXX9_NDFMCR_CE 0x10 3862306a36Sopenharmony_ci#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ 3962306a36Sopenharmony_ci#define TXX9_NDFMCR_ALE 0x02 4062306a36Sopenharmony_ci#define TXX9_NDFMCR_CLE 0x01 4162306a36Sopenharmony_ci/* TX4939 only */ 4262306a36Sopenharmony_ci#define TXX9_NDFMCR_X16 0x0400 4362306a36Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 4462306a36Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 4562306a36Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_128 0x0100 4662306a36Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_256 0x0200 4762306a36Sopenharmony_ci#define TXX9_NDFMCR_DMAREQ_512 0x0300 4862306a36Sopenharmony_ci#define TXX9_NDFMCR_CS_MASK 0x0c 4962306a36Sopenharmony_ci#define TXX9_NDFMCR_CS(ch) ((ch) << 2) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* NDFMCR : NDFMC Status */ 5262306a36Sopenharmony_ci#define TXX9_NDFSR_BUSY 0x80 5362306a36Sopenharmony_ci/* TX4939 only */ 5462306a36Sopenharmony_ci#define TXX9_NDFSR_DMARUN 0x40 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* NDFMCR : NDFMC Reset */ 5762306a36Sopenharmony_ci#define TXX9_NDFRSTR_RST 0x01 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct txx9ndfmc_priv { 6062306a36Sopenharmony_ci struct platform_device *dev; 6162306a36Sopenharmony_ci struct nand_chip chip; 6262306a36Sopenharmony_ci int cs; 6362306a36Sopenharmony_ci const char *mtdname; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define MAX_TXX9NDFMC_DEV 4 6762306a36Sopenharmony_cistruct txx9ndfmc_drvdata { 6862306a36Sopenharmony_ci struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; 6962306a36Sopenharmony_ci void __iomem *base; 7062306a36Sopenharmony_ci unsigned char hold; /* in gbusclock */ 7162306a36Sopenharmony_ci unsigned char spw; /* in gbusclock */ 7262306a36Sopenharmony_ci struct nand_controller controller; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct platform_device *mtd_to_platdev(struct mtd_info *mtd) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 7862306a36Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); 7962306a36Sopenharmony_ci return txx9_priv->dev; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 8562306a36Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return drvdata->base + (reg << plat->shift); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci return __raw_readl(ndregaddr(dev, reg)); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void txx9ndfmc_write(struct platform_device *dev, 9662306a36Sopenharmony_ci u32 val, unsigned int reg) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci __raw_writel(val, ndregaddr(dev, reg)); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic uint8_t txx9ndfmc_read_byte(struct nand_chip *chip) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return txx9ndfmc_read(dev, TXX9_NDFDTR); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void txx9ndfmc_write_buf(struct nand_chip *chip, const uint8_t *buf, 10962306a36Sopenharmony_ci int len) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 11262306a36Sopenharmony_ci void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); 11362306a36Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); 11662306a36Sopenharmony_ci while (len--) 11762306a36Sopenharmony_ci __raw_writel(*buf++, ndfdtr); 11862306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void txx9ndfmc_read_buf(struct nand_chip *chip, uint8_t *buf, int len) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 12462306a36Sopenharmony_ci void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci while (len--) 12762306a36Sopenharmony_ci *buf++ = __raw_readl(ndfdtr); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void txx9ndfmc_cmd_ctrl(struct nand_chip *chip, int cmd, 13162306a36Sopenharmony_ci unsigned int ctrl) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip); 13462306a36Sopenharmony_ci struct platform_device *dev = txx9_priv->dev; 13562306a36Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (ctrl & NAND_CTRL_CHANGE) { 13862306a36Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); 14162306a36Sopenharmony_ci mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; 14262306a36Sopenharmony_ci mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; 14362306a36Sopenharmony_ci /* TXX9_NDFMCR_CE bit is 0:high 1:low */ 14462306a36Sopenharmony_ci mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; 14562306a36Sopenharmony_ci if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { 14662306a36Sopenharmony_ci mcr &= ~TXX9_NDFMCR_CS_MASK; 14762306a36Sopenharmony_ci mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (cmd != NAND_CMD_NONE) 15262306a36Sopenharmony_ci txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); 15362306a36Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { 15462306a36Sopenharmony_ci /* dummy write to update external latch */ 15562306a36Sopenharmony_ci if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) 15662306a36Sopenharmony_ci txx9ndfmc_write(dev, 0, TXX9_NDFDTR); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int txx9ndfmc_dev_ready(struct nand_chip *chip) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int txx9ndfmc_calculate_ecc(struct nand_chip *chip, const uint8_t *dat, 16862306a36Sopenharmony_ci uint8_t *ecc_code) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 17162306a36Sopenharmony_ci int eccbytes; 17262306a36Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci mcr &= ~TXX9_NDFMCR_ECC_ALL; 17562306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 17662306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); 17762306a36Sopenharmony_ci for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { 17862306a36Sopenharmony_ci ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); 17962306a36Sopenharmony_ci ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); 18062306a36Sopenharmony_ci ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); 18162306a36Sopenharmony_ci ecc_code += 3; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf, 18862306a36Sopenharmony_ci unsigned char *read_ecc, 18962306a36Sopenharmony_ci unsigned char *calc_ecc) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int eccsize; 19262306a36Sopenharmony_ci int corrected = 0; 19362306a36Sopenharmony_ci int stat; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { 19662306a36Sopenharmony_ci stat = rawnand_sw_hamming_correct(chip, buf, read_ecc, 19762306a36Sopenharmony_ci calc_ecc); 19862306a36Sopenharmony_ci if (stat < 0) 19962306a36Sopenharmony_ci return stat; 20062306a36Sopenharmony_ci corrected += stat; 20162306a36Sopenharmony_ci buf += 256; 20262306a36Sopenharmony_ci read_ecc += 3; 20362306a36Sopenharmony_ci calc_ecc += 3; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci return corrected; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void txx9ndfmc_enable_hwecc(struct nand_chip *chip, int mode) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip)); 21162306a36Sopenharmony_ci u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mcr &= ~TXX9_NDFMCR_ECC_ALL; 21462306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); 21562306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); 21662306a36Sopenharmony_ci txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void txx9ndfmc_initialize(struct platform_device *dev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 22262306a36Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 22362306a36Sopenharmony_ci int tmout = 100; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) 22662306a36Sopenharmony_ci ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ 22762306a36Sopenharmony_ci else { 22862306a36Sopenharmony_ci /* reset NDFMC */ 22962306a36Sopenharmony_ci txx9ndfmc_write(dev, 23062306a36Sopenharmony_ci txx9ndfmc_read(dev, TXX9_NDFRSTR) | 23162306a36Sopenharmony_ci TXX9_NDFRSTR_RST, 23262306a36Sopenharmony_ci TXX9_NDFRSTR); 23362306a36Sopenharmony_ci while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { 23462306a36Sopenharmony_ci if (--tmout == 0) { 23562306a36Sopenharmony_ci dev_err(&dev->dev, "reset failed.\n"); 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci udelay(1); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci /* setup Hold Time, Strobe Pulse Width */ 24262306a36Sopenharmony_ci txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); 24362306a36Sopenharmony_ci txx9ndfmc_write(dev, 24462306a36Sopenharmony_ci (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? 24562306a36Sopenharmony_ci TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ 24962306a36Sopenharmony_ci DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int txx9ndfmc_attach_chip(struct nand_chip *chip) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci chip->ecc.strength = 1; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (mtd->writesize >= 512) { 26162306a36Sopenharmony_ci chip->ecc.size = 512; 26262306a36Sopenharmony_ci chip->ecc.bytes = 6; 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci chip->ecc.size = 256; 26562306a36Sopenharmony_ci chip->ecc.bytes = 3; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci chip->ecc.calculate = txx9ndfmc_calculate_ecc; 26962306a36Sopenharmony_ci chip->ecc.correct = txx9ndfmc_correct_data; 27062306a36Sopenharmony_ci chip->ecc.hwctl = txx9ndfmc_enable_hwecc; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic const struct nand_controller_ops txx9ndfmc_controller_ops = { 27662306a36Sopenharmony_ci .attach_chip = txx9ndfmc_attach_chip, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int __init txx9ndfmc_probe(struct platform_device *dev) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); 28262306a36Sopenharmony_ci int hold, spw; 28362306a36Sopenharmony_ci int i; 28462306a36Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata; 28562306a36Sopenharmony_ci unsigned long gbusclk = plat->gbus_clock; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); 28862306a36Sopenharmony_ci if (!drvdata) 28962306a36Sopenharmony_ci return -ENOMEM; 29062306a36Sopenharmony_ci drvdata->base = devm_platform_ioremap_resource(dev, 0); 29162306a36Sopenharmony_ci if (IS_ERR(drvdata->base)) 29262306a36Sopenharmony_ci return PTR_ERR(drvdata->base); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci hold = plat->hold ?: 20; /* tDH */ 29562306a36Sopenharmony_ci spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); 29862306a36Sopenharmony_ci spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); 29962306a36Sopenharmony_ci if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) 30062306a36Sopenharmony_ci hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ 30162306a36Sopenharmony_ci spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ 30262306a36Sopenharmony_ci hold = clamp(hold, 1, 15); 30362306a36Sopenharmony_ci drvdata->hold = hold; 30462306a36Sopenharmony_ci spw = clamp(spw, 1, 15); 30562306a36Sopenharmony_ci drvdata->spw = spw; 30662306a36Sopenharmony_ci dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", 30762306a36Sopenharmony_ci (gbusclk + 500000) / 1000000, hold, spw); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci nand_controller_init(&drvdata->controller); 31062306a36Sopenharmony_ci drvdata->controller.ops = &txx9ndfmc_controller_ops; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci platform_set_drvdata(dev, drvdata); 31362306a36Sopenharmony_ci txx9ndfmc_initialize(dev); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { 31662306a36Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv; 31762306a36Sopenharmony_ci struct nand_chip *chip; 31862306a36Sopenharmony_ci struct mtd_info *mtd; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!(plat->ch_mask & (1 << i))) 32162306a36Sopenharmony_ci continue; 32262306a36Sopenharmony_ci txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), 32362306a36Sopenharmony_ci GFP_KERNEL); 32462306a36Sopenharmony_ci if (!txx9_priv) 32562306a36Sopenharmony_ci continue; 32662306a36Sopenharmony_ci chip = &txx9_priv->chip; 32762306a36Sopenharmony_ci mtd = nand_to_mtd(chip); 32862306a36Sopenharmony_ci mtd->dev.parent = &dev->dev; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci chip->legacy.read_byte = txx9ndfmc_read_byte; 33162306a36Sopenharmony_ci chip->legacy.read_buf = txx9ndfmc_read_buf; 33262306a36Sopenharmony_ci chip->legacy.write_buf = txx9ndfmc_write_buf; 33362306a36Sopenharmony_ci chip->legacy.cmd_ctrl = txx9ndfmc_cmd_ctrl; 33462306a36Sopenharmony_ci chip->legacy.dev_ready = txx9ndfmc_dev_ready; 33562306a36Sopenharmony_ci chip->legacy.chip_delay = 100; 33662306a36Sopenharmony_ci chip->controller = &drvdata->controller; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci nand_set_controller_data(chip, txx9_priv); 33962306a36Sopenharmony_ci txx9_priv->dev = dev; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (plat->ch_mask != 1) { 34262306a36Sopenharmony_ci txx9_priv->cs = i; 34362306a36Sopenharmony_ci txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", 34462306a36Sopenharmony_ci dev_name(&dev->dev), i); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci txx9_priv->cs = -1; 34762306a36Sopenharmony_ci txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), 34862306a36Sopenharmony_ci GFP_KERNEL); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci if (!txx9_priv->mtdname) { 35162306a36Sopenharmony_ci kfree(txx9_priv); 35262306a36Sopenharmony_ci dev_err(&dev->dev, "Unable to allocate MTD name.\n"); 35362306a36Sopenharmony_ci continue; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (plat->wide_mask & (1 << i)) 35662306a36Sopenharmony_ci chip->options |= NAND_BUSWIDTH_16; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (nand_scan(chip, 1)) { 35962306a36Sopenharmony_ci kfree(txx9_priv->mtdname); 36062306a36Sopenharmony_ci kfree(txx9_priv); 36162306a36Sopenharmony_ci continue; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci mtd->name = txx9_priv->mtdname; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci mtd_device_register(mtd, NULL, 0); 36662306a36Sopenharmony_ci drvdata->mtds[i] = mtd; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int __exit txx9ndfmc_remove(struct platform_device *dev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); 37562306a36Sopenharmony_ci int ret, i; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!drvdata) 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { 38062306a36Sopenharmony_ci struct mtd_info *mtd = drvdata->mtds[i]; 38162306a36Sopenharmony_ci struct nand_chip *chip; 38262306a36Sopenharmony_ci struct txx9ndfmc_priv *txx9_priv; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!mtd) 38562306a36Sopenharmony_ci continue; 38662306a36Sopenharmony_ci chip = mtd_to_nand(mtd); 38762306a36Sopenharmony_ci txx9_priv = nand_get_controller_data(chip); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 39062306a36Sopenharmony_ci WARN_ON(ret); 39162306a36Sopenharmony_ci nand_cleanup(chip); 39262306a36Sopenharmony_ci kfree(txx9_priv->mtdname); 39362306a36Sopenharmony_ci kfree(txx9_priv); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci#ifdef CONFIG_PM 39962306a36Sopenharmony_cistatic int txx9ndfmc_resume(struct platform_device *dev) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci if (platform_get_drvdata(dev)) 40262306a36Sopenharmony_ci txx9ndfmc_initialize(dev); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci#else 40662306a36Sopenharmony_ci#define txx9ndfmc_resume NULL 40762306a36Sopenharmony_ci#endif 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic struct platform_driver txx9ndfmc_driver = { 41062306a36Sopenharmony_ci .remove = __exit_p(txx9ndfmc_remove), 41162306a36Sopenharmony_ci .resume = txx9ndfmc_resume, 41262306a36Sopenharmony_ci .driver = { 41362306a36Sopenharmony_ci .name = "txx9ndfmc", 41462306a36Sopenharmony_ci }, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cimodule_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 42062306a36Sopenharmony_ciMODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); 42162306a36Sopenharmony_ciMODULE_ALIAS("platform:txx9ndfmc"); 422