[RFC] mtd: add OF2PDEV convertor for the NDFC driver

Sebastian Siewior bigeasy at linutronix.de
Wed Aug 20 16:59:58 EDT 2008


I didn't convert the NDFC driver to support OF because there are
non-OF-aware platforms with the ndfc chip.
All settings are mandatory except the oob layout.

Signed-off-by: Sebastian Siewior <bigeasy at linutronix.de>
---
 drivers/mtd/nand/Kconfig   |    7 ++
 drivers/mtd/nand/Makefile  |    1 +
 drivers/mtd/nand/ndfc_of.c |  253 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 261 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/ndfc_of.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ab0d77e..5bf0a25 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -170,6 +170,13 @@ config MTD_NAND_NDFC
 	help
 	 NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
 
+config MTD_NAND_NDFC_OF
+	tristate "OF support for NDFC"
+	depends on MTD_NAND_NDFC && PPC_OF
+	help
+	  This setting allows to read the NanD configuration from OF instead
+	  of a Platform device.
+
 config MTD_NAND_S3C2410_CLKSTOP
 	bool "S3C2410 NAND IDLE clock stop"
 	depends on MTD_NAND_S3C2410
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index b786c5d..5a9da0f 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_MTD_NAND_TS7250)		+= ts7250.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
+obj-$(CONFIG_MTD_NAND_NDFC_OF)		+= ndfc_of.o
 obj-$(CONFIG_MTD_NAND_ATMEL)		+= atmel_nand.o
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
diff --git a/drivers/mtd/nand/ndfc_of.c b/drivers/mtd/nand/ndfc_of.c
new file mode 100644
index 0000000..852dca3
--- /dev/null
+++ b/drivers/mtd/nand/ndfc_of.c
@@ -0,0 +1,253 @@
+/*
+ * OF -> Platform device wrapper for NDFC
+ * (c) Sebastian Siewior, Linutronix
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/ndfc.h>
+
+struct resource ndfc_resource;
+
+struct ndfc_controller_settings ndfc_settings;
+
+struct platform_nand_ctrl ndfc_nand_ctrl = {
+	.priv = &ndfc_settings,
+};
+
+static struct platform_device ndfc_nand_device = {
+	.name = "ndfc-nand",
+	.id = 0,
+	.dev = {
+		.platform_data = &ndfc_nand_ctrl,
+	},
+	.num_resources = 1,
+	.resource = &ndfc_resource,
+};
+
+static int ndfc_num_devices;
+
+struct of_ndfc_devices {
+	struct platform_device device;
+	struct platform_nand_chip chip;
+	struct ndfc_chip_settings chip_settings;
+	struct nand_ecclayout ecclayout;
+};
+
+static struct of_ndfc_devices *ndfc_chips;
+
+static int __devinit of_nand_probe(struct of_device *dev,
+		const struct of_device_id *match)
+{
+	struct device_node *dp = dev->node;
+	struct device_node *child_node;
+	const u32 *u32_val;
+	int ret;
+	int i;
+	u32 len;
+	u32 ccr_bank;
+	u32 read_cycles;
+	u32 num_chips = 0;
+
+	ret = of_address_to_resource(dp, 0, &ndfc_resource);
+	if (ret) {
+		dev_err(&dev->dev, "Failed to read address from DT.\n");
+		return ret;
+	}
+
+	u32_val = of_get_property(dp, "id", &len);
+	if (!u32_val || len != 4) {
+		dev_err(&dev->dev, "Failed to read property: id\n");
+		return -EINVAL;
+	}
+	ndfc_nand_device.id = *u32_val;
+
+	u32_val = of_get_property(dp, "ccr_bank", &len);
+	if (!u32_val || len != 4) {
+		dev_err(&dev->dev, "Failed to read property: ccr_bank\n");
+		return -EINVAL;
+	}
+	ccr_bank = *u32_val;
+
+	u32_val = of_get_property(dp, "read_cycles", &len);
+	if (!u32_val || len != 4) {
+		dev_err(&dev->dev, "Failed to read property: read_cycles\n");
+		return -EINVAL;
+	}
+	read_cycles = *u32_val;
+
+	ndfc_settings.ccr_settings = NDFC_CCR_BS(ccr_bank) | read_cycles;
+
+	u32_val = of_get_property(dp, "erpn", &len);
+	if (!u32_val || len != 4) {
+		dev_err(&dev->dev, "Failed to read property: erpn\n");
+		return -EINVAL;
+	}
+	ndfc_settings.ndfc_erpn = *u32_val;
+
+	for_each_child_of_node(dp, child_node)
+			num_chips++;
+
+	if (!num_chips) {
+		dev_err(&dev->dev, "Failed to find chip nodes\n");
+		return -EINVAL;
+	}
+
+	ndfc_num_devices = num_chips;
+	ndfc_chips = kzalloc(ndfc_num_devices * sizeof(struct of_ndfc_devices),
+			GFP_KERNEL);
+	if (!ndfc_chips) {
+		dev_err(&dev->dev, "OOM while allocating memory for %d "
+				"ndfc_chips\n", num_chips);
+		return -ENOMEM;
+	}
+
+	num_chips = 0;
+	for_each_child_of_node(dp, child_node) {
+		struct of_ndfc_devices *cur_dev;
+		struct mtd_partition *partitions;
+		int num_part;
+
+		BUG_ON(num_chips > ndfc_num_devices);
+		cur_dev = &ndfc_chips[num_chips];
+
+		num_part = of_mtd_parse_partitions(&dev->dev, child_node,
+				&partitions);
+		if (num_part < 0) {
+			dev_err(&dev->dev, "Failed to parse partition table %d "
+					"from OF\n", num_chips);
+			goto out_err;
+		}
+
+		u32_val = of_get_property(child_node, "chipoffset", &len);
+		if (!u32_val || len != 4) {
+			dev_err(&dev->dev, "Failed to read property: chipoffset\n");
+			goto out_err;
+		}
+		cur_dev->chip.chip_offset = *u32_val;
+
+		u32_val = of_get_property(child_node, "delay", &len);
+		if (!u32_val || len != 4) {
+			dev_err(&dev->dev, "Failed to read property: delay\n");
+			goto out_err;
+		}
+		cur_dev->chip.chip_delay = *u32_val;
+
+		u32_val = of_get_property(child_node, "banksettings", &len);
+		if (!u32_val || len != 4) {
+			dev_err(&dev->dev, "Failed to read property: banksettings\n");
+			goto out_err;
+		}
+		cur_dev->chip_settings.bank_settings = *u32_val;
+
+		cur_dev->device.name = "ndfc-chip";
+		cur_dev->device.id = num_chips;
+		cur_dev->device.num_resources = 1;
+		cur_dev->device.resource = &ndfc_resource;
+
+		cur_dev->device.dev.platform_data = &cur_dev->chip;
+		cur_dev->device.dev.parent = &ndfc_nand_device.dev;
+
+		u32_val = of_get_property(child_node, "eccbytes", &len);
+		if (!u32_val)
+			goto skip_oob_data;
+
+		cur_dev->ecclayout.eccbytes = *u32_val;
+
+		u32_val = of_get_property(child_node, "eccpos", &len);
+		if (!u32_val)
+			goto skip_oob_data;
+
+		len /= sizeof(u32);
+		if (len > 64)
+			goto skip_oob_data;
+
+		for (i = 0; i < len; i++)
+			cur_dev->ecclayout.eccpos[i] = u32_val[i];
+
+		u32_val = of_get_property(child_node, "oobfree", &len);
+		if (!u32_val)
+			goto skip_oob_data;
+
+		len /= sizeof(u32);
+		if (len & 1)
+			goto skip_oob_data;
+
+		if (len / 2 > MTD_MAX_OOBFREE_ENTRIES)
+			goto skip_oob_data;
+
+		for (i = 0; i < len; i += 2) {
+			cur_dev->ecclayout.oobfree[i / 2].offset =
+				u32_val[i];
+			cur_dev->ecclayout.oobfree[i / 2].length =
+				u32_val[i + 1];
+		}
+
+		cur_dev->chip.ecclayout = &cur_dev->ecclayout;
+skip_oob_data:
+		cur_dev->chip.nr_partitions = num_part;
+		cur_dev->chip.partitions = partitions;
+
+		cur_dev->chip.priv = &cur_dev->chip_settings;
+
+		num_chips++;
+	}
+
+	platform_device_register(&ndfc_nand_device);
+	for (i = 0; i < ndfc_num_devices; i++)
+		platform_device_register(&ndfc_chips[i].device);
+	return 0;
+
+out_err:
+	for (i = 0; i < num_chips; i++)
+		kfree(ndfc_chips[i].chip.partitions);
+	kfree(ndfc_chips);
+	ndfc_chips = NULL;
+	return -EINVAL;
+}
+
+static int of_nand_remove(struct of_device *dev)
+{
+	int i;
+
+	for (i = 0; i < ndfc_num_devices; i++)
+		platform_device_unregister(&ndfc_chips[i].device);
+	platform_device_unregister(&ndfc_nand_device);
+	kfree(ndfc_chips);
+	ndfc_chips = NULL;
+	return 0;
+}
+
+static struct of_device_id of_nand_match[] = {
+	{
+		.compatible = "ibm,ndfc",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_nand_match);
+
+static struct of_platform_driver of_flash_driver = {
+	.name           = "of-ibm-ndfc",
+	.match_table    = of_nand_match,
+	.probe          = of_nand_probe,
+	.remove         = of_nand_remove,
+};
+
+static int __init agco_nand_init(void)
+{
+	return of_register_platform_driver(&of_flash_driver);
+}
+
+static void __exit agco_nand_exit(void)
+{
+	of_unregister_platform_driver(&of_flash_driver);
+}
+
+module_init(agco_nand_init);
+module_exit(agco_nand_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sebastian Siewior <bigeasy at linutronix.de>");
+MODULE_DESCRIPTION("OF 2 PDEV converter for ndfc");
-- 
1.5.5.2




More information about the linux-mtd mailing list