mirror of https://github.com/hak5/openwrt.git
256 lines
6.7 KiB
Diff
256 lines
6.7 KiB
Diff
--- /dev/null
|
|
+++ b/drivers/mtd/nand/rbppc_nand.c
|
|
@@ -0,0 +1,252 @@
|
|
+/*
|
|
+ * Copyright (C) 2008-2009 Noah Fontes <nfontes@transtruct.org>
|
|
+ * Copyright (C) 2009 Michael Guntsche <mike@it-loops.com>
|
|
+ * 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 <linux/init.h>
|
|
+#include <linux/mtd/nand.h>
|
|
+#include <linux/mtd/mtd.h>
|
|
+#include <linux/mtd/partitions.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <asm/of_platform.h>
|
|
+#include <asm/of_device.h>
|
|
+#include <linux/delay.h>
|
|
+#include <asm/io.h>
|
|
+
|
|
+#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 <https://dev.openwrt.org/browser/trunk/target/linux/rb532/
|
|
+ * patches-2.6.28/025-rb532_nand_fixup.patch> 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 rbppc_nand_probe(struct of_device *pdev,
|
|
+ const struct of_device_id *match)
|
|
+{
|
|
+ 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 600 series NAND driver, version " DRV_VERSION "\n");
|
|
+
|
|
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
|
|
+
|
|
+ rdy = of_get_property(pdev->node, "rdy", NULL);
|
|
+ nce = of_get_property(pdev->node, "nce", NULL);
|
|
+ cle = of_get_property(pdev->node, "cle", NULL);
|
|
+ ale = of_get_property(pdev->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->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;
|
|
+ }
|
|
+
|
|
+ add_mtd_partitions(&rmtd, 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 of_platform_driver rbppc_nand_driver = {
|
|
+ .name = "nand",
|
|
+ .probe = rbppc_nand_probe,
|
|
+ .match_table = rbppc_nand_ids,
|
|
+ .driver = {
|
|
+ .name = "rbppc-nand",
|
|
+ .owner = THIS_MODULE,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init rbppc_nand_init(void)
|
|
+{
|
|
+ return of_register_platform_driver(&rbppc_nand_driver);
|
|
+}
|
|
+
|
|
+static void __exit rbppc_nand_exit(void)
|
|
+{
|
|
+ of_unregister_platform_driver(&rbppc_nand_driver);
|
|
+}
|
|
+
|
|
+MODULE_AUTHOR("Mikrotikls SIA");
|
|
+MODULE_AUTHOR("Noah Fontes");
|
|
+MODULE_AUTHOR("Michael Guntsche");
|
|
+MODULE_DESCRIPTION("MikroTik RouterBOARD 600 series NAND driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_VERSION(DRV_VERSION);
|
|
+
|
|
+module_init(rbppc_nand_init);
|
|
+module_exit(rbppc_nand_exit);
|