[RFC][POWERPC] Add arch/powerpc 4xx NDFC (NAND) support
David Gibson
david at gibson.dropbear.id.au
Wed Oct 10 16:38:57 EST 2007
On Wed, Oct 10, 2007 at 08:32:12AM +0200, Stefan Roese wrote:
> This patch makes the PPC4xx NAND flash controller (NDFC) device-tree
> friendly using OF glue code to create and insert necessary platform
> devices. Such "constructor" approach makes NAND usable under
> arch/powerpc yet keeping full compatibility with arch/ppc.
>
> This patch also introduces a "common" (not NOR only)
> of_parse_flash_partitions() routine in mtdpart.c that can/should be
> used by all drivers parsing device-tree partition informations. The
> current implementation is not compatible with the current physmap_of
> version and needs some additional work to make it really usable from
> both "drivers", physmap_of and ndfc_of. I'm just posting it right now
> to get some feedback, since this stuff is already sitting here too
> long on my disk and waiting for upstream merge.
>
> Any feedback welcome. Thanks.
>
> Signed-off-by: Stefan Roese <sr at denx.de>
>
> ---
> commit 721a340398e66872b9cc7e8b630fc92a7681ca04
> tree ffbe1194146cb4fc324755f35c9062025b7ec0f6
> parent 26f571d7c968dbd30656fc1421eeb0d9088aaad9
> author Stefan Roese <sr at denx.de> Mon, 08 Oct 2007 16:00:49 +0200
> committer Stefan Roese <sr at denx.de> Mon, 08 Oct 2007 16:00:49 +0200
>
> arch/powerpc/boot/dts/sequoia.dts | 32 +++++++
> arch/powerpc/platforms/44x/Makefile | 6 +
> arch/powerpc/platforms/44x/ndfc_of.c | 158 ++++++++++++++++++++++++++++++++++
> drivers/mtd/mtdpart.c | 61 +++++++++++++
> drivers/mtd/nand/ndfc.c | 6 +
> include/linux/mtd/partitions.h | 2
> 6 files changed, 264 insertions(+), 1 deletions(-)
>
> diff --git a/arch/powerpc/boot/dts/sequoia.dts b/arch/powerpc/boot/dts/sequoia.dts
> index 36be75b..9b15482 100644
> --- a/arch/powerpc/boot/dts/sequoia.dts
> +++ b/arch/powerpc/boot/dts/sequoia.dts
> @@ -122,6 +122,38 @@
> interrupt-map-mask = <ffffffff>;
> };
>
> + ndfc at 1d0000000, {
> + device_type = "nand";
Ditch the device_type. There's no call for it here.
> + compatible = "ibm,ndfc";
This should probably have a more specific value for the revision in
addition to ibm,ndfc.
> + reg = <1 d0000000 2000>;
> +
> + #address-cells = <1>;
> + #size-cells = <1>;
> + /* ndfc stuff, composed off ndfc_settings. */
> + /* select bank on CE[3], 4 Addr, 1 Col 3 Row 512b page */
> + ccr-settings = <3001000>;
> +
> + chip at 0 {
> + device_type = "nand-chip";
Ditch this device_type too, it makes absolutely no sense here. You
probably should have a compatible, though.
> + reg = <0 1>;
I don't really know how the ndfc works. Can the reg size here ever be
anything other than 1 sensibly? If not, then you should set
#size-cells=0 instead.
> + chip-nr = <1>;
> + chip-offset = <3>;
> + chip-delay = <50>;
> + chip-bank-settings = <80002222>;
> +
> + /* normal NAND ECC stuff */
> + ecc-bytes = <6>;
> + ecc-pos = <0 1 2 3 6 7>;
> + /* list of tuples assumed here */
> + ecc-oobfree = <8 8>;
> +
> + partition at 0 {
Ok, the partitions really are per-chip, not across the controller's
domain as a whole? Oh and if this is here, then the chip needs
#address-cells and #size-cells.
> + label = "content";
> + reg = <0 0>;
> + };
> + };
> + };
> +
> POB0: opb {
> compatible = "ibm,opb-440epx", "ibm,opb";
> #address-cells = <1>;
> diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile
> index 10ce674..d6195ee 100644
> --- a/arch/powerpc/platforms/44x/Makefile
> +++ b/arch/powerpc/platforms/44x/Makefile
> @@ -1,4 +1,8 @@
> obj-$(CONFIG_44x) := misc_44x.o
> obj-$(CONFIG_EBONY) += ebony.o
> -obj-$(CONFIG_BAMBOO) += bamboo.o
> +obj-$(CONFIG_BAMBOO) += bamboo.o
> obj-$(CONFIG_SEQUOIA) += sequoia.o
> +
> +ifeq ($(CONFIG_MTD_NAND_NDFC),y)
> +obj-y += ndfc_of.o
> +endif
> diff --git a/arch/powerpc/platforms/44x/ndfc_of.c b/arch/powerpc/platforms/44x/ndfc_of.c
> new file mode 100644
> index 0000000..e5b41cf
> --- /dev/null
> +++ b/arch/powerpc/platforms/44x/ndfc_of.c
> @@ -0,0 +1,158 @@
> +/*
> + * PPC4xx NAND wrapper from device tree to platform device
> + *
> + * Stefan Roese <sr at denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/stddef.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/ndfc.h>
> +#include <linux/of.h>
> +
> +static struct ndfc_controller_settings ndfc_settings;
> +static struct platform_nand_ctrl nand_ctrl = {
> + .priv = &ndfc_settings,
> +};
> +
> +static struct ndfc_chip_settings chip_settings;
> +static struct nand_ecclayout ecclayout;
> +static struct mtd_partition *nand_parts;
> +
> +static struct platform_nand_chip nand_chip = {
> + .ecclayout = &ecclayout,
> + .priv = &chip_settings,
> +};
> +
> +static struct resource r;
> +
> +static struct platform_device ndfc_dev = {
> + .name = "ndfc-nand",
> + .id = 0,
> + .num_resources = 1,
> + .resource = &r,
> + .dev = {
> + .platform_data = &nand_ctrl,
> + }
> +};
> +
> +static struct platform_device nand_dev = {
> + .name = "ndfc-chip",
> + .id = 0,
> + .num_resources = 1,
> + .resource = &r,
> + .dev = {
> + .platform_data = &nand_chip,
> + .parent = &ndfc_dev.dev,
> + }
> +};
> +
> +/* Until this will be settled */
> +static inline u32 of_get_int(struct device_node *np, const char *name)
> +{
> + unsigned int size;
> + const u32 *prop = of_get_property(np, name, &size);
> +
> + if ((prop == NULL) || (size != sizeof(int))) {
> + printk(KERN_WARNING "%s property missing!\n", __FUNCTION__);
> + return 0;
> + }
> +
> + return *prop;
> +}
> +
> +static int ppc4xx_setup_nand_chip_node(struct device_node *dev)
> +{
> + unsigned int what = -ENODEV;
> + unsigned int size, amnt;
> + const u32 *prop;
> + int i;
> +
> + /* process necessary properties */
> + what = of_get_int(dev, "chip-nr");
> + nand_chip.nr_chips = what;
> +
> + what = of_get_int(dev, "chip-offset");
> + nand_chip.chip_offset = what;
> +
> + what = of_get_int(dev, "chip-delay");
> + nand_chip.chip_delay = what;
> +
> + what = of_get_int(dev, "ecc-bytes");
> + ecclayout.eccbytes = what;
> +
> + what = of_get_int(dev, "chip-bank-settings");
> + chip_settings.bank_settings = what;
> +
> + prop = of_get_property(dev, "ecc-pos", &size);
> + for (i = 0; i < (size/sizeof(unsigned int)); i++)
> + ecclayout.eccpos[i] = prop[i];
> +
> + prop = of_get_property(dev, "ecc-oobfree", &size);
> + amnt = size/sizeof(unsigned int);
> +
> + for (i = 0; i < amnt; i += 2) {
> + nand_chip.ecclayout->oobfree[i].offset = prop[i];
> + nand_chip.ecclayout->oobfree[i].length = prop[i+1];
> + }
> +
> + nand_chip.nr_partitions = of_parse_flash_partitions(dev, &nand_parts);
> + nand_chip.partitions = nand_parts;
> +
> + return 0;
> +}
> +
> +static int __init ppc4xx_setup_nand_node(struct device_node *dev)
> +{
> + struct device_node *child = NULL;
> + int ret = 0;
> +
> + memset(&r, 0, sizeof(r));
> +
> + /* generic NDFC register */
> + ret = of_address_to_resource(dev, 0, &r);
> + if (ret)
> + goto err;
> +
> + /* Now let's create platform_data stuff based on dts entries */
> + ret = of_get_int(dev, "ccr-settings");
> +
> + ndfc_settings.ccr_settings = ret;
> + ndfc_settings.ndfc_erpn = r.start & 0xf00000000ULL;
> +
> + child = of_get_next_child(dev, NULL);
> + /* NAND platform device is sole, so assuming one child of ndfc node */
> + if (child != NULL)
> + ppc4xx_setup_nand_chip_node(child);
> +
> + ndfc_dev.resource = &r;
> + nand_dev.resource = &r;
> +
> + platform_device_register(&ndfc_dev);
> + platform_device_register(&nand_dev);
> +
> +err:
> + return ret;
> +}
> +
> +static int ppc4xx_init_nand(void)
> +{
> + struct device_node *np =
> + of_find_compatible_node(NULL, "nand", "ibm,ndfc");
> +
> + if (np != NULL)
> + ppc4xx_setup_nand_node(np);
> +
> + return 0;
> +}
> +arch_initcall(ppc4xx_init_nand);
> diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
> index 6174a97..cc620ee 100644
> --- a/drivers/mtd/mtdpart.c
> +++ b/drivers/mtd/mtdpart.c
> @@ -21,6 +21,10 @@
> #include <linux/mtd/partitions.h>
> #include <linux/mtd/compatmac.h>
>
> +#ifdef CONFIG_PPC_MERGE
> +#include <linux/of.h>
> +#endif
> +
> /* Our partition linked list */
> static LIST_HEAD(mtd_partitions);
>
> @@ -557,6 +561,63 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
> return ret;
> }
>
> +#ifdef CONFIG_PPC_MERGE
> +int of_parse_flash_partitions(struct device_node *dp,
> + struct mtd_partition **mparts)
> +{
> + int nr_parts = 0;
> + int i;
> + struct device_node *pp;
> + const char *partname;
> + struct mtd_partition *parts;
> +
> + /* First count the subnodes */
> + for (pp = dp->child; pp; pp = pp->sibling)
> + nr_parts++;
> +
> + if (nr_parts) {
> + parts = kzalloc(nr_parts * sizeof(struct mtd_partition),
> + GFP_KERNEL);
> + if (!parts) {
> + printk(KERN_ERR
> + "Can't allocate the flash partition data!\n");
> + return -ENOMEM;
> + }
> +
> + for (pp = dp->child, i = 0 ; pp; pp = pp->sibling, i++) {
> + const u32 *reg;
> + int len;
> +
> + reg = of_get_property(pp, "reg", &len);
> + if (!reg || (len != 2*sizeof(u32))) {
> + printk(KERN_ERR "Invalid 'reg' on %s\n",
> + dp->full_name);
> + kfree(parts);
> + parts = NULL;
> + return -EINVAL;
> + }
> + parts[i].offset = reg[0];
> + parts[i].size = reg[1];
> +
> + partname = of_get_property(pp, "label", &len);
> + if (!partname)
> + partname = of_get_property(pp, "name", &len);
> + parts[i].name = (char *)partname;
> + if (of_get_property(pp, "read-only", &len))
> + parts[i].mask_flags = MTD_WRITEABLE;
> + (*mparts) = parts;
> + }
> + } else {
> + printk(KERN_ERR
> + "Node %s does not seem to contain partitions definition!\n",
> + dp->full_name);
> + return -EINVAL;
> + }
> +
> + return nr_parts;
> +}
> +#endif
> +
> EXPORT_SYMBOL_GPL(parse_mtd_partitions);
> EXPORT_SYMBOL_GPL(register_mtd_parser);
> EXPORT_SYMBOL_GPL(deregister_mtd_parser);
> diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
> index fd7a8d5..7901019 100644
> --- a/drivers/mtd/nand/ndfc.c
> +++ b/drivers/mtd/nand/ndfc.c
> @@ -24,7 +24,9 @@
> #include <linux/platform_device.h>
>
> #include <asm/io.h>
> +#ifndef CONFIG_PPC_MERGE
> #include <asm/ibm44x.h>
> +#endif
>
> struct ndfc_nand_mtd {
> struct mtd_info mtd;
> @@ -230,7 +232,11 @@ static int ndfc_nand_probe(struct platform_device *pdev)
> struct ndfc_controller *ndfc = &ndfc_ctrl;
> unsigned long long phys = settings->ndfc_erpn | res->start;
>
> +#if !defined(CONFIG_PHYS_64BIT) || defined(CONFIG_PPC_MERGE)
> + ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
> +#else
> ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
> +#endif
> if (!ndfc->ndfcbase) {
> printk(KERN_ERR "NDFC: ioremap failed\n");
> return -EIO;
> diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
> index da6b3d6..546a098 100644
> --- a/include/linux/mtd/partitions.h
> +++ b/include/linux/mtd/partitions.h
> @@ -68,6 +68,8 @@ extern int register_mtd_parser(struct mtd_part_parser *parser);
> extern int deregister_mtd_parser(struct mtd_part_parser *parser);
> extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
> struct mtd_partition **pparts, unsigned long origin);
> +extern int of_parse_flash_partitions(struct device_node *node,
> + struct mtd_partition **parts);
>
> #define put_partition_parser(p) do { module_put((p)->owner); } while(0)
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
More information about the Linuxppc-dev
mailing list