brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · 37f79c0 Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TXx9 NAND flash memory controller driver4 * Based on RBTX49xx patch from CELF patch archive.5 *6 * (C) Copyright TOSHIBA CORPORATION 2004-20077 * All Rights Reserved.8 */9#include <linux/err.h>10#include <linux/init.h>11#include <linux/slab.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/delay.h>15#include <linux/mtd/mtd.h>16#include <linux/mtd/rawnand.h>17#include <linux/mtd/partitions.h>18#include <linux/io.h>19#include <linux/platform_data/txx9/ndfmc.h>20 21/* TXX9 NDFMC Registers */22#define TXX9_NDFDTR	0x0023#define TXX9_NDFMCR	0x0424#define TXX9_NDFSR	0x0825#define TXX9_NDFISR	0x0c26#define TXX9_NDFIMR	0x1027#define TXX9_NDFSPR	0x1428#define TXX9_NDFRSTR	0x18	/* not TX4939 */29 30/* NDFMCR : NDFMC Mode Control */31#define TXX9_NDFMCR_WE	0x8032#define TXX9_NDFMCR_ECC_ALL	0x6033#define TXX9_NDFMCR_ECC_RESET	0x6034#define TXX9_NDFMCR_ECC_READ	0x4035#define TXX9_NDFMCR_ECC_ON	0x2036#define TXX9_NDFMCR_ECC_OFF	0x0037#define TXX9_NDFMCR_CE	0x1038#define TXX9_NDFMCR_BSPRT	0x04	/* TX4925/TX4926 only */39#define TXX9_NDFMCR_ALE	0x0240#define TXX9_NDFMCR_CLE	0x0141/* TX4939 only */42#define TXX9_NDFMCR_X16	0x040043#define TXX9_NDFMCR_DMAREQ_MASK	0x030044#define TXX9_NDFMCR_DMAREQ_NODMA	0x000045#define TXX9_NDFMCR_DMAREQ_128	0x010046#define TXX9_NDFMCR_DMAREQ_256	0x020047#define TXX9_NDFMCR_DMAREQ_512	0x030048#define TXX9_NDFMCR_CS_MASK	0x0c49#define TXX9_NDFMCR_CS(ch)	((ch) << 2)50 51/* NDFMCR : NDFMC Status */52#define TXX9_NDFSR_BUSY	0x8053/* TX4939 only */54#define TXX9_NDFSR_DMARUN	0x4055 56/* NDFMCR : NDFMC Reset */57#define TXX9_NDFRSTR_RST	0x0158 59struct txx9ndfmc_priv {60	struct platform_device *dev;61	struct nand_chip chip;62	int cs;63	const char *mtdname;64};65 66#define MAX_TXX9NDFMC_DEV	467struct txx9ndfmc_drvdata {68	struct mtd_info *mtds[MAX_TXX9NDFMC_DEV];69	void __iomem *base;70	unsigned char hold;	/* in gbusclock */71	unsigned char spw;	/* in gbusclock */72	struct nand_controller controller;73};74 75static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)76{77	struct nand_chip *chip = mtd_to_nand(mtd);78	struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip);79	return txx9_priv->dev;80}81 82static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)83{84	struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);85	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);86 87	return drvdata->base + (reg << plat->shift);88}89 90static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg)91{92	return __raw_readl(ndregaddr(dev, reg));93}94 95static void txx9ndfmc_write(struct platform_device *dev,96			    u32 val, unsigned int reg)97{98	__raw_writel(val, ndregaddr(dev, reg));99}100 101static uint8_t txx9ndfmc_read_byte(struct nand_chip *chip)102{103	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));104 105	return txx9ndfmc_read(dev, TXX9_NDFDTR);106}107 108static void txx9ndfmc_write_buf(struct nand_chip *chip, const uint8_t *buf,109				int len)110{111	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));112	void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);113	u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);114 115	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR);116	while (len--)117		__raw_writel(*buf++, ndfdtr);118	txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);119}120 121static void txx9ndfmc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)122{123	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));124	void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);125 126	while (len--)127		*buf++ = __raw_readl(ndfdtr);128}129 130static void txx9ndfmc_cmd_ctrl(struct nand_chip *chip, int cmd,131			       unsigned int ctrl)132{133	struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip);134	struct platform_device *dev = txx9_priv->dev;135	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);136 137	if (ctrl & NAND_CTRL_CHANGE) {138		u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);139 140		mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE);141		mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0;142		mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0;143		/* TXX9_NDFMCR_CE bit is 0:high 1:low */144		mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0;145		if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) {146			mcr &= ~TXX9_NDFMCR_CS_MASK;147			mcr |= TXX9_NDFMCR_CS(txx9_priv->cs);148		}149		txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);150	}151	if (cmd != NAND_CMD_NONE)152		txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR);153	if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) {154		/* dummy write to update external latch */155		if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE)156			txx9ndfmc_write(dev, 0, TXX9_NDFDTR);157	}158}159 160static int txx9ndfmc_dev_ready(struct nand_chip *chip)161{162	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));163 164	return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY);165}166 167static int txx9ndfmc_calculate_ecc(struct nand_chip *chip, const uint8_t *dat,168				   uint8_t *ecc_code)169{170	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));171	int eccbytes;172	u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);173 174	mcr &= ~TXX9_NDFMCR_ECC_ALL;175	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);176	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);177	for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) {178		ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);179		ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);180		ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);181		ecc_code += 3;182	}183	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);184	return 0;185}186 187static int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf,188				  unsigned char *read_ecc,189				  unsigned char *calc_ecc)190{191	int eccsize;192	int corrected = 0;193	int stat;194 195	for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {196		stat = rawnand_sw_hamming_correct(chip, buf, read_ecc,197						  calc_ecc);198		if (stat < 0)199			return stat;200		corrected += stat;201		buf += 256;202		read_ecc += 3;203		calc_ecc += 3;204	}205	return corrected;206}207 208static void txx9ndfmc_enable_hwecc(struct nand_chip *chip, int mode)209{210	struct platform_device *dev = mtd_to_platdev(nand_to_mtd(chip));211	u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);212 213	mcr &= ~TXX9_NDFMCR_ECC_ALL;214	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR);215	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);216	txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR);217}218 219static void txx9ndfmc_initialize(struct platform_device *dev)220{221	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);222	struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);223	int tmout = 100;224 225	if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR)226		; /* no NDFRSTR.  Write to NDFSPR resets the NDFMC. */227	else {228		/* reset NDFMC */229		txx9ndfmc_write(dev,230				txx9ndfmc_read(dev, TXX9_NDFRSTR) |231				TXX9_NDFRSTR_RST,232				TXX9_NDFRSTR);233		while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) {234			if (--tmout == 0) {235				dev_err(&dev->dev, "reset failed.\n");236				break;237			}238			udelay(1);239		}240	}241	/* setup Hold Time, Strobe Pulse Width */242	txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR);243	txx9ndfmc_write(dev,244			(plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ?245			TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR);246}247 248#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \249	DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)250 251static int txx9ndfmc_attach_chip(struct nand_chip *chip)252{253	struct mtd_info *mtd = nand_to_mtd(chip);254 255	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)256		return 0;257 258	chip->ecc.strength = 1;259 260	if (mtd->writesize >= 512) {261		chip->ecc.size = 512;262		chip->ecc.bytes = 6;263	} else {264		chip->ecc.size = 256;265		chip->ecc.bytes = 3;266	}267 268	chip->ecc.calculate = txx9ndfmc_calculate_ecc;269	chip->ecc.correct = txx9ndfmc_correct_data;270	chip->ecc.hwctl = txx9ndfmc_enable_hwecc;271 272	return 0;273}274 275static const struct nand_controller_ops txx9ndfmc_controller_ops = {276	.attach_chip = txx9ndfmc_attach_chip,277};278 279static int txx9ndfmc_probe(struct platform_device *dev)280{281	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);282	int hold, spw;283	int i;284	struct txx9ndfmc_drvdata *drvdata;285	unsigned long gbusclk = plat->gbus_clock;286 287	drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);288	if (!drvdata)289		return -ENOMEM;290	drvdata->base = devm_platform_ioremap_resource(dev, 0);291	if (IS_ERR(drvdata->base))292		return PTR_ERR(drvdata->base);293 294	hold = plat->hold ?: 20; /* tDH */295	spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */296 297	hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold);298	spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw);299	if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD)300		hold -= 2;	/* actual hold time : (HOLD + 2) BUSCLK */301	spw -= 1;	/* actual wait time : (SPW + 1) BUSCLK */302	hold = clamp(hold, 1, 15);303	drvdata->hold = hold;304	spw = clamp(spw, 1, 15);305	drvdata->spw = spw;306	dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",307		 (gbusclk + 500000) / 1000000, hold, spw);308 309	nand_controller_init(&drvdata->controller);310	drvdata->controller.ops = &txx9ndfmc_controller_ops;311 312	platform_set_drvdata(dev, drvdata);313	txx9ndfmc_initialize(dev);314 315	for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {316		struct txx9ndfmc_priv *txx9_priv;317		struct nand_chip *chip;318		struct mtd_info *mtd;319 320		if (!(plat->ch_mask & (1 << i)))321			continue;322		txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv),323				    GFP_KERNEL);324		if (!txx9_priv)325			continue;326		chip = &txx9_priv->chip;327		mtd = nand_to_mtd(chip);328		mtd->dev.parent = &dev->dev;329 330		chip->legacy.read_byte = txx9ndfmc_read_byte;331		chip->legacy.read_buf = txx9ndfmc_read_buf;332		chip->legacy.write_buf = txx9ndfmc_write_buf;333		chip->legacy.cmd_ctrl = txx9ndfmc_cmd_ctrl;334		chip->legacy.dev_ready = txx9ndfmc_dev_ready;335		chip->legacy.chip_delay = 100;336		chip->controller = &drvdata->controller;337 338		nand_set_controller_data(chip, txx9_priv);339		txx9_priv->dev = dev;340 341		if (plat->ch_mask != 1) {342			txx9_priv->cs = i;343			txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u",344						       dev_name(&dev->dev), i);345		} else {346			txx9_priv->cs = -1;347			txx9_priv->mtdname = kstrdup(dev_name(&dev->dev),348						     GFP_KERNEL);349		}350		if (!txx9_priv->mtdname) {351			kfree(txx9_priv);352			dev_err(&dev->dev, "Unable to allocate MTD name.\n");353			continue;354		}355		if (plat->wide_mask & (1 << i))356			chip->options |= NAND_BUSWIDTH_16;357 358		if (nand_scan(chip, 1)) {359			kfree(txx9_priv->mtdname);360			kfree(txx9_priv);361			continue;362		}363		mtd->name = txx9_priv->mtdname;364 365		mtd_device_register(mtd, NULL, 0);366		drvdata->mtds[i] = mtd;367	}368 369	return 0;370}371 372static void txx9ndfmc_remove(struct platform_device *dev)373{374	struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);375	int ret, i;376 377	for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {378		struct mtd_info *mtd = drvdata->mtds[i];379		struct nand_chip *chip;380		struct txx9ndfmc_priv *txx9_priv;381 382		if (!mtd)383			continue;384		chip = mtd_to_nand(mtd);385		txx9_priv = nand_get_controller_data(chip);386 387		ret = mtd_device_unregister(nand_to_mtd(chip));388		WARN_ON(ret);389		nand_cleanup(chip);390		kfree(txx9_priv->mtdname);391		kfree(txx9_priv);392	}393}394 395#ifdef CONFIG_PM396static int txx9ndfmc_resume(struct platform_device *dev)397{398	if (platform_get_drvdata(dev))399		txx9ndfmc_initialize(dev);400	return 0;401}402#else403#define txx9ndfmc_resume NULL404#endif405 406static struct platform_driver txx9ndfmc_driver = {407	.probe		= txx9ndfmc_probe,408	.remove_new	= txx9ndfmc_remove,409	.resume		= txx9ndfmc_resume,410	.driver		= {411		.name	= "txx9ndfmc",412	},413};414module_platform_driver(txx9ndfmc_driver);415 416MODULE_LICENSE("GPL");417MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");418MODULE_ALIAS("platform:txx9ndfmc");419