diff options
Diffstat (limited to 'drivers/mtd/nand/davinci_nand.c')
| -rw-r--r-- | drivers/mtd/nand/davinci_nand.c | 274 | 
1 files changed, 141 insertions, 133 deletions
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index a90fde3ede2..b922c8efcf4 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -24,7 +24,6 @@   */  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/err.h> @@ -33,12 +32,12 @@  #include <linux/mtd/nand.h>  #include <linux/mtd/partitions.h>  #include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_mtd.h> -#include <mach/nand.h> -#include <mach/aemif.h> - -#include <asm/mach-types.h> - +#include <linux/platform_data/mtd-davinci.h> +#include <linux/platform_data/mtd-davinci-aemif.h>  /*   * This is a device driver for the NAND flash controller found on the @@ -60,7 +59,6 @@ struct davinci_nand_info {  	struct device		*dev;  	struct clk		*clk; -	bool			partitioned;  	bool			is_readmode; @@ -489,7 +487,7 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd)   * ten ECC bytes plus the manufacturer's bad block marker byte, and   * and not overlapping the default BBT markers.   */ -static struct nand_ecclayout hwecc4_small __initconst = { +static struct nand_ecclayout hwecc4_small = {  	.eccbytes = 10,  	.eccpos = { 0, 1, 2, 3, 4,  		/* offset 5 holds the badblock marker */ @@ -505,7 +503,7 @@ static struct nand_ecclayout hwecc4_small __initconst = {   * storing ten ECC bytes plus the manufacturer's bad block marker byte,   * and not overlapping the default BBT markers.   */ -static struct nand_ecclayout hwecc4_2048 __initconst = { +static struct nand_ecclayout hwecc4_2048 = {  	.eccbytes = 40,  	.eccpos = {  		/* at the end of spare sector */ @@ -522,9 +520,88 @@ static struct nand_ecclayout hwecc4_2048 __initconst = {  	},  }; -static int __init nand_davinci_probe(struct platform_device *pdev) +#if defined(CONFIG_OF) +static const struct of_device_id davinci_nand_of_match[] = { +	{.compatible = "ti,davinci-nand", }, +	{.compatible = "ti,keystone-nand", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, davinci_nand_of_match); + +static struct davinci_nand_pdata +	*nand_davinci_get_pdata(struct platform_device *pdev) +{ +	if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { +		struct davinci_nand_pdata *pdata; +		const char *mode; +		u32 prop; + +		pdata =  devm_kzalloc(&pdev->dev, +				sizeof(struct davinci_nand_pdata), +				GFP_KERNEL); +		pdev->dev.platform_data = pdata; +		if (!pdata) +			return ERR_PTR(-ENOMEM); +		if (!of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-chipselect", &prop)) +			pdev->id = prop; +		else +			return ERR_PTR(-EINVAL); + +		if (!of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-mask-ale", &prop)) +			pdata->mask_ale = prop; +		if (!of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-mask-cle", &prop)) +			pdata->mask_cle = prop; +		if (!of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-mask-chipsel", &prop)) +			pdata->mask_chipsel = prop; +		if (!of_property_read_string(pdev->dev.of_node, +			"nand-ecc-mode", &mode) || +		    !of_property_read_string(pdev->dev.of_node, +			"ti,davinci-ecc-mode", &mode)) { +			if (!strncmp("none", mode, 4)) +				pdata->ecc_mode = NAND_ECC_NONE; +			if (!strncmp("soft", mode, 4)) +				pdata->ecc_mode = NAND_ECC_SOFT; +			if (!strncmp("hw", mode, 2)) +				pdata->ecc_mode = NAND_ECC_HW; +		} +		if (!of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-ecc-bits", &prop)) +			pdata->ecc_bits = prop; + +		prop = of_get_nand_bus_width(pdev->dev.of_node); +		if (0 < prop || !of_property_read_u32(pdev->dev.of_node, +			"ti,davinci-nand-buswidth", &prop)) +			if (prop == 16) +				pdata->options |= NAND_BUSWIDTH_16; +		if (of_property_read_bool(pdev->dev.of_node, +			"nand-on-flash-bbt") || +		    of_property_read_bool(pdev->dev.of_node, +			"ti,davinci-nand-use-bbt")) +			pdata->bbt_options = NAND_BBT_USE_FLASH; + +		if (of_device_is_compatible(pdev->dev.of_node, +					    "ti,keystone-nand")) { +			pdata->options |= NAND_NO_SUBPAGE_WRITE; +		} +	} + +	return dev_get_platdata(&pdev->dev); +} +#else +static struct davinci_nand_pdata +	*nand_davinci_get_pdata(struct platform_device *pdev) +{ +	return dev_get_platdata(&pdev->dev); +} +#endif + +static int nand_davinci_probe(struct platform_device *pdev)  { -	struct davinci_nand_pdata	*pdata = pdev->dev.platform_data; +	struct davinci_nand_pdata	*pdata;  	struct davinci_nand_info	*info;  	struct resource			*res1;  	struct resource			*res2; @@ -534,6 +611,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  	uint32_t			val;  	nand_ecc_modes_t		ecc_mode; +	pdata = nand_davinci_get_pdata(pdev); +	if (IS_ERR(pdata)) +		return PTR_ERR(pdata); +  	/* insist on board-specific configuration */  	if (!pdata)  		return -ENODEV; @@ -542,12 +623,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  	if (pdev->id < 0 || pdev->id > 3)  		return -ENODEV; -	info = kzalloc(sizeof(*info), GFP_KERNEL); -	if (!info) { -		dev_err(&pdev->dev, "unable to allocate memory\n"); -		ret = -ENOMEM; -		goto err_nomem; -	} +	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); +	if (!info) +		return -ENOMEM;  	platform_set_drvdata(pdev, info); @@ -555,16 +633,23 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  	res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);  	if (!res1 || !res2) {  		dev_err(&pdev->dev, "resource missing\n"); -		ret = -EINVAL; -		goto err_nomem; +		return -EINVAL;  	} -	vaddr = ioremap(res1->start, resource_size(res1)); -	base = ioremap(res2->start, resource_size(res2)); -	if (!vaddr || !base) { -		dev_err(&pdev->dev, "ioremap failed\n"); -		ret = -EINVAL; -		goto err_ioremap; +	vaddr = devm_ioremap_resource(&pdev->dev, res1); +	if (IS_ERR(vaddr)) +		return PTR_ERR(vaddr); + +	/* +	 * This registers range is used to setup NAND settings. In case with +	 * TI AEMIF driver, the same memory address range is requested already +	 * by AEMIF, so we cannot request it twice, just ioremap. +	 * The AEMIF and NAND drivers not use the same registers in this range. +	 */ +	base = devm_ioremap(&pdev->dev, res2->start, resource_size(res2)); +	if (!base) { +		dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res2); +		return -EADDRNOTAVAIL;  	}  	info->dev		= &pdev->dev; @@ -582,7 +667,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  	info->chip.chip_delay	= 0;  	info->chip.select_chip	= nand_davinci_select_chip; -	/* options such as NAND_USE_FLASH_BBT or 16-bit widths */ +	/* options such as NAND_BBT_USE_FLASH */ +	info->chip.bbt_options	= pdata->bbt_options; +	/* options such as 16-bit widths */  	info->chip.options	= pdata->options;  	info->chip.bbt_td	= pdata->bbt_td;  	info->chip.bbt_md	= pdata->bbt_md; @@ -630,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  			spin_unlock_irq(&davinci_nand_lock);  			if (ret == -EBUSY) -				goto err_ecc; +				return ret;  			info->chip.ecc.calculate = nand_davinci_calculate_4bit;  			info->chip.ecc.correct = nand_davinci_correct_4bit; @@ -643,47 +730,27 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  			info->chip.ecc.bytes = 3;  		}  		info->chip.ecc.size = 512; +		info->chip.ecc.strength = pdata->ecc_bits;  		break;  	default: -		ret = -EINVAL; -		goto err_ecc; +		return -EINVAL;  	}  	info->chip.ecc.mode = ecc_mode; -	info->clk = clk_get(&pdev->dev, "aemif"); +	info->clk = devm_clk_get(&pdev->dev, "aemif");  	if (IS_ERR(info->clk)) {  		ret = PTR_ERR(info->clk);  		dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); -		goto err_clk; +		return ret;  	} -	ret = clk_enable(info->clk); +	ret = clk_prepare_enable(info->clk);  	if (ret < 0) {  		dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",  			ret);  		goto err_clk_enable;  	} -	/* -	 * Setup Async configuration register in case we did not boot from -	 * NAND and so bootloader did not bother to set it up. -	 */ -	val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4); - -	/* Extended Wait is not valid and Select Strobe mode is not used */ -	val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK); -	if (info->chip.options & NAND_BUSWIDTH_16) -		val |= 0x1; - -	davinci_nand_writel(info, A1CR_OFFSET + info->core_chipsel * 4, val); - -	ret = davinci_aemif_setup_timing(info->timing, info->base, -							info->core_chipsel); -	if (ret < 0) { -		dev_dbg(&pdev->dev, "NAND timing values setup fail\n"); -		goto err_timing; -	} -  	spin_lock_irq(&davinci_nand_lock);  	/* put CSxNAND into NAND mode */ @@ -697,7 +764,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  	ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL);  	if (ret < 0) {  		dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); -		goto err_scan; +		goto err;  	}  	/* Update ECC layout if needed ... for 1-bit HW ECC, the default @@ -711,7 +778,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  		if (!chunks || info->mtd.oobsize < 16) {  			dev_dbg(&pdev->dev, "too small\n");  			ret = -EINVAL; -			goto err_scan; +			goto err;  		}  		/* For small page chips, preserve the manufacturer's @@ -742,7 +809,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)  		dev_warn(&pdev->dev, "no 4-bit ECC support yet "  				"for 4KiB-page NAND\n");  		ret = -EIO; -		goto err_scan; +		goto err;  syndrome_done:  		info->chip.ecc.layout = &info->ecclayout; @@ -750,46 +817,20 @@ syndrome_done:  	ret = nand_scan_tail(&info->mtd);  	if (ret < 0) -		goto err_scan; - -	if (mtd_has_partitions()) { -		struct mtd_partition	*mtd_parts = NULL; -		int			mtd_parts_nb = 0; +		goto err; -		if (mtd_has_cmdlinepart()) { -			static const char *probes[] __initconst = -				{ "cmdlinepart", NULL }; +	if (pdata->parts) +		ret = mtd_device_parse_register(&info->mtd, NULL, NULL, +					pdata->parts, pdata->nr_parts); +	else { +		struct mtd_part_parser_data	ppdata; -			mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, -							    &mtd_parts, 0); -		} - -		if (mtd_parts_nb <= 0) { -			mtd_parts = pdata->parts; -			mtd_parts_nb = pdata->nr_parts; -		} - -		/* Register any partitions */ -		if (mtd_parts_nb > 0) { -			ret = add_mtd_partitions(&info->mtd, -					mtd_parts, mtd_parts_nb); -			if (ret == 0) -				info->partitioned = true; -		} - -	} else if (pdata->nr_parts) { -		dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", -				pdata->nr_parts, info->mtd.name); +		ppdata.of_node = pdev->dev.of_node; +		ret = mtd_device_parse_register(&info->mtd, NULL, &ppdata, +						NULL, 0);  	} - -	/* If there's no partition info, just package the whole chip -	 * as a single MTD device. -	 */ -	if (!info->partitioned) -		ret = add_mtd_device(&info->mtd) ? -ENODEV : 0; -  	if (ret < 0) -		goto err_scan; +		goto err;  	val = davinci_nand_readl(info, NRCSR_OFFSET);  	dev_info(&pdev->dev, "controller rev. %d.%d\n", @@ -797,78 +838,45 @@ syndrome_done:  	return 0; -err_scan: -err_timing: -	clk_disable(info->clk); +err: +	clk_disable_unprepare(info->clk);  err_clk_enable: -	clk_put(info->clk); -  	spin_lock_irq(&davinci_nand_lock);  	if (ecc_mode == NAND_ECC_HW_SYNDROME)  		ecc4_busy = false;  	spin_unlock_irq(&davinci_nand_lock); - -err_ecc: -err_clk: -err_ioremap: -	if (base) -		iounmap(base); -	if (vaddr) -		iounmap(vaddr); - -err_nomem: -	kfree(info);  	return ret;  } -static int __exit nand_davinci_remove(struct platform_device *pdev) +static int nand_davinci_remove(struct platform_device *pdev)  {  	struct davinci_nand_info *info = platform_get_drvdata(pdev); -	int status; - -	if (mtd_has_partitions() && info->partitioned) -		status = del_mtd_partitions(&info->mtd); -	else -		status = del_mtd_device(&info->mtd);  	spin_lock_irq(&davinci_nand_lock);  	if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)  		ecc4_busy = false;  	spin_unlock_irq(&davinci_nand_lock); -	iounmap(info->base); -	iounmap(info->vaddr); -  	nand_release(&info->mtd); -	clk_disable(info->clk); -	clk_put(info->clk); - -	kfree(info); +	clk_disable_unprepare(info->clk);  	return 0;  }  static struct platform_driver nand_davinci_driver = { -	.remove		= __exit_p(nand_davinci_remove), +	.probe		= nand_davinci_probe, +	.remove		= nand_davinci_remove,  	.driver		= {  		.name	= "davinci_nand", +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(davinci_nand_of_match),  	},  };  MODULE_ALIAS("platform:davinci_nand"); -static int __init nand_davinci_init(void) -{ -	return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe); -} -module_init(nand_davinci_init); - -static void __exit nand_davinci_exit(void) -{ -	platform_driver_unregister(&nand_davinci_driver); -} -module_exit(nand_davinci_exit); +module_platform_driver(nand_davinci_driver);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Texas Instruments");  | 
