--- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -436,6 +436,13 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_NAND_RB_PPC + tristate "MikroTik RB333/600 NAND support" + depends on RB_PPC + help + This option enables support for the NAND device on MikroTik + RouterBOARD 333/600 series boards. + config MTD_ALAUDA tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1" depends on USB --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx27 obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +obj-$(CONFIG_MTD_NAND_RB_PPC) += rbppc_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o --- /dev/null +++ b/drivers/mtd/nand/rbppc_nand.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2008-2009 Noah Fontes + * Copyright (C) 2009 Michael Guntsche + * Copyright (C) Mikrotik 2007 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rbppc_nand" +#define DRV_VERSION "0.0.2" + +static struct mtd_info rmtd; +static struct nand_chip rnand; + +struct rbppc_nand_info { + void *gpi; + void *gpo; + void *localbus; + + unsigned gpio_rdy; + unsigned gpio_nce; + unsigned gpio_cle; + unsigned gpio_ale; + unsigned gpio_ctrls; +}; + +/* We must use the OOB layout from yaffs 1 if we want this to be recognized + * properly. Borrowed from the OpenWRT patches for the RB532. + * + * See for more details. + */ +static struct nand_ecclayout rbppc_nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; + +static struct mtd_partition rbppc_nand_partition_info[] = { + { + name: "kernel", + offset: 0, + size: 4 * 1024 * 1024, + }, + { + name: "rootfs", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL, + }, +}; + +static int rbppc_nand_dev_ready(struct mtd_info *mtd) { + struct nand_chip *chip = mtd->priv; + struct rbppc_nand_info *priv = chip->priv; + + return in_be32(priv->gpi) & priv->gpio_rdy; +} + +static void rbppc_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { + struct nand_chip *chip = mtd->priv; + struct rbppc_nand_info *priv = chip->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned val = in_be32(priv->gpo); + if (!(val & priv->gpio_nce)) { + /* make sure Local Bus has done NAND operations */ + readb(priv->localbus); + } + + if (ctrl & NAND_CLE) { + val |= priv->gpio_cle; + } else { + val &= ~priv->gpio_cle; + } + if (ctrl & NAND_ALE) { + val |= priv->gpio_ale; + } else { + val &= ~priv->gpio_ale; + } + if (!(ctrl & NAND_NCE)) { + val |= priv->gpio_nce; + } else { + val &= ~priv->gpio_nce; + } + out_be32(priv->gpo, val); + + /* make sure GPIO output has changed */ + val ^= in_be32(priv->gpo); + if (val & priv->gpio_ctrls) { + printk(KERN_ERR "rbppc_nand_hwcontrol: NAND GPO change failed 0x%08x\n", val); + } + } + + if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); +} + +static void rbppc_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + memcpy(buf, chip->IO_ADDR_R, len); +} + +static unsigned init_ok = 0; + +static int __devinit rbppc_nand_probe(struct platform_device *pdev) +{ + struct device_node *gpio; + struct device_node *nnand; + struct resource res; + struct rbppc_nand_info *info; + void *baddr; + const unsigned *rdy, *nce, *cle, *ale; + + printk(KERN_INFO "rbppc_nand_probe: MikroTik RouterBOARD 333/600 series NAND driver, version " DRV_VERSION "\n"); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + + rdy = of_get_property(pdev->dev.of_node, "rdy", NULL); + nce = of_get_property(pdev->dev.of_node, "nce", NULL); + cle = of_get_property(pdev->dev.of_node, "cle", NULL); + ale = of_get_property(pdev->dev.of_node, "ale", NULL); + + if (!rdy || !nce || !cle || !ale) { + printk(KERN_ERR "rbppc_nand_probe: GPIO properties are missing\n"); + goto err; + } + if (rdy[0] != nce[0] || rdy[0] != cle[0] || rdy[0] != ale[0]) { + printk(KERN_ERR "rbppc_nand_probe: Different GPIOs are not supported\n"); + goto err; + } + + gpio = of_find_node_by_phandle(rdy[0]); + if (!gpio) { + printk(KERN_ERR "rbppc_nand_probe: No GPIO<%x> node found\n", *rdy); + goto err; + } + if (of_address_to_resource(gpio, 0, &res)) { + printk(KERN_ERR "rbppc_nand_probe: No reg property in GPIO found\n"); + goto err; + } + info->gpo = ioremap_nocache(res.start, res.end - res.start + 1); + + if (!of_address_to_resource(gpio, 1, &res)) { + info->gpi = ioremap_nocache(res.start, res.end - res.start + 1); + } else { + info->gpi = info->gpo; + } + of_node_put(gpio); + + info->gpio_rdy = 1 << (31 - rdy[1]); + info->gpio_nce = 1 << (31 - nce[1]); + info->gpio_cle = 1 << (31 - cle[1]); + info->gpio_ale = 1 << (31 - ale[1]); + info->gpio_ctrls = info->gpio_nce | info->gpio_cle | info->gpio_ale; + + nnand = of_find_node_by_name(NULL, "nnand"); + if (!nnand) { + printk("rbppc_nand_probe: No nNAND found\n"); + goto err; + } + if (of_address_to_resource(nnand, 0, &res)) { + printk("rbppc_nand_probe: No reg property in nNAND found\n"); + goto err; + } + of_node_put(nnand); + info->localbus = ioremap_nocache(res.start, res.end - res.start + 1); + + if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { + printk("rbppc_nand_probe: No reg property found\n"); + goto err; + } + baddr = ioremap_nocache(res.start, res.end - res.start + 1); + + memset(&rnand, 0, sizeof(rnand)); + rnand.cmd_ctrl = rbppc_nand_cmd_ctrl; + rnand.dev_ready = rbppc_nand_dev_ready; + rnand.read_buf = rbppc_nand_read_buf; + rnand.IO_ADDR_W = baddr; + rnand.IO_ADDR_R = baddr; + rnand.priv = info; + + memset(&rmtd, 0, sizeof(rmtd)); + rnand.ecc.mode = NAND_ECC_SOFT; + rnand.ecc.layout = &rbppc_nand_oob_16; + rnand.chip_delay = 25; + rnand.options |= NAND_NO_AUTOINCR; + rmtd.priv = &rnand; + rmtd.owner = THIS_MODULE; + + if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) { + printk(KERN_ERR "rbppc_nand_probe: RouterBOARD NAND device not found\n"); + return -ENXIO; + } + + mtd_device_parse_register(&rmtd, NULL, 0, rbppc_nand_partition_info, 2); + init_ok = 1; + return 0; + +err: + kfree(info); + return -1; +} + +static struct of_device_id rbppc_nand_ids[] = { + { .name = "nand", }, + { }, +}; + +static struct platform_driver rbppc_nand_driver = { + .probe = rbppc_nand_probe, + .driver = { + .name = "rbppc-nand", + .owner = THIS_MODULE, + .of_match_table = rbppc_nand_ids, + }, +}; + +static int __init rbppc_nand_init(void) +{ + return platform_driver_register(&rbppc_nand_driver); +} + +static void __exit rbppc_nand_exit(void) +{ + platform_driver_unregister(&rbppc_nand_driver); +} + +MODULE_AUTHOR("Mikrotikls SIA"); +MODULE_AUTHOR("Noah Fontes"); +MODULE_AUTHOR("Michael Guntsche"); +MODULE_DESCRIPTION("MikroTik RouterBOARD 333/600 series NAND driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(rbppc_nand_init); +module_exit(rbppc_nand_exit);