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