| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * NAND Flash Controller Device Driver for DT | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright © 2011, Picochip. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms and conditions of the GNU General Public License, | 
					
						
							|  |  |  |  * version 2, as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope it will be useful, but WITHOUT | 
					
						
							|  |  |  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
					
						
							|  |  |  |  * more details. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/clk.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/ioport.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/of.h>
 | 
					
						
							|  |  |  | #include <linux/of_device.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "denali.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct denali_dt { | 
					
						
							|  |  |  | 	struct denali_nand_info	denali; | 
					
						
							|  |  |  | 	struct clk		*clk; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __iomem *request_and_map(struct device *dev, | 
					
						
							|  |  |  | 				     const struct resource *res) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void __iomem *ptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!devm_request_mem_region(dev, res->start, resource_size(res), | 
					
						
							|  |  |  | 				     "denali-dt")) { | 
					
						
							|  |  |  | 		dev_err(dev, "unable to request %s\n", res->name); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ptr = devm_ioremap_nocache(dev, res->start, resource_size(res)); | 
					
						
							| 
									
										
										
										
											2013-03-18 15:11:11 +05:30
										 |  |  | 	if (!ptr) | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | 		dev_err(dev, "ioremap_nocache of %s failed!", res->name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct of_device_id denali_nand_dt_ids[] = { | 
					
						
							|  |  |  | 		{ .compatible = "denali,denali-nand-dt" }, | 
					
						
							|  |  |  | 		{ /* sentinel */ } | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u64 denali_dma_mask; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 13:23:07 -05:00
										 |  |  | static int denali_dt_probe(struct platform_device *ofdev) | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct resource *denali_reg, *nand_data; | 
					
						
							|  |  |  | 	struct denali_dt *dt; | 
					
						
							|  |  |  | 	struct denali_nand_info *denali; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	const struct of_device_id *of_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev); | 
					
						
							|  |  |  | 	if (of_id) { | 
					
						
							|  |  |  | 		ofdev->id_entry = of_id->data; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pr_err("Failed to find the right device id.\n"); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!dt) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	denali = &dt->denali; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); | 
					
						
							|  |  |  | 	nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); | 
					
						
							|  |  |  | 	if (!denali_reg || !nand_data) { | 
					
						
							|  |  |  | 		dev_err(&ofdev->dev, "resources not completely defined\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	denali->platform = DT; | 
					
						
							|  |  |  | 	denali->dev = &ofdev->dev; | 
					
						
							|  |  |  | 	denali->irq = platform_get_irq(ofdev, 0); | 
					
						
							|  |  |  | 	if (denali->irq < 0) { | 
					
						
							|  |  |  | 		dev_err(&ofdev->dev, "no irq defined\n"); | 
					
						
							| 
									
										
										
										
											2013-03-18 15:11:13 +05:30
										 |  |  | 		return denali->irq; | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	denali->flash_reg = request_and_map(&ofdev->dev, denali_reg); | 
					
						
							|  |  |  | 	if (!denali->flash_reg) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	denali->flash_mem = request_and_map(&ofdev->dev, nand_data); | 
					
						
							|  |  |  | 	if (!denali->flash_mem) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!of_property_read_u32(ofdev->dev.of_node, | 
					
						
							|  |  |  | 		"dma-mask", (u32 *)&denali_dma_mask)) { | 
					
						
							|  |  |  | 		denali->dev->dma_mask = &denali_dma_mask; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		denali->dev->dma_mask = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dt->clk = clk_get(&ofdev->dev, NULL); | 
					
						
							|  |  |  | 	if (IS_ERR(dt->clk)) { | 
					
						
							|  |  |  | 		dev_err(&ofdev->dev, "no clk available\n"); | 
					
						
							|  |  |  | 		return PTR_ERR(dt->clk); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	clk_prepare_enable(dt->clk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = denali_init(denali); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out_disable_clk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	platform_set_drvdata(ofdev, dt); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_disable_clk: | 
					
						
							|  |  |  | 	clk_disable_unprepare(dt->clk); | 
					
						
							|  |  |  | 	clk_put(dt->clk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 13:26:04 -05:00
										 |  |  | static int denali_dt_remove(struct platform_device *ofdev) | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct denali_dt *dt = platform_get_drvdata(ofdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	denali_remove(&dt->denali); | 
					
						
							|  |  |  | 	clk_disable(dt->clk); | 
					
						
							|  |  |  | 	clk_put(dt->clk); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver denali_dt_driver = { | 
					
						
							|  |  |  | 	.probe		= denali_dt_probe, | 
					
						
							| 
									
										
										
										
											2012-11-19 13:21:24 -05:00
										 |  |  | 	.remove		= denali_dt_remove, | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | 	.driver		= { | 
					
						
							|  |  |  | 		.name	= "denali-nand-dt", | 
					
						
							|  |  |  | 		.owner	= THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2013-03-18 15:11:14 +05:30
										 |  |  | 		.of_match_table	= denali_nand_dt_ids, | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-18 15:11:12 +05:30
										 |  |  | module_platform_driver(denali_dt_driver); | 
					
						
							| 
									
										
										
										
											2012-09-27 10:58:06 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Jamie Iles"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("DT driver for Denali NAND controller"); |