]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Add SMI NAND driver
authorLuke Wren <wren6991@gmail.com>
Sat, 5 Sep 2015 00:16:10 +0000 (01:16 +0100)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 19 Sep 2017 10:07:42 +0000 (12:07 +0200)
Signed-off-by: Luke Wren <wren6991@gmail.com>
Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt [new file with mode: 0644]
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/bcm2835_smi_nand.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt b/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt
new file mode 100644 (file)
index 0000000..159544d
--- /dev/null
@@ -0,0 +1,42 @@
+* BCM2835 SMI NAND flash
+
+This driver is a shim between the BCM2835 SMI driver (SMI is a peripheral for
+talking to parallel register interfaces) and Linux's MTD layer.
+
+Required properties:
+- compatible: "brcm,bcm2835-smi-nand"
+- status: "okay"
+
+Optional properties:
+- partition@n, where n is an integer from a consecutive sequence starting at 0
+       - Difficult to store partition table on NAND device - normally put it
+       in the source code, kernel bootparams, or device tree (the best way!)
+       - Sub-properties:
+               - label: the partition name, as shown by mtdinfo /dev/mtd*
+               - reg: the size and offset of this partition.
+               - (optional) read-only: an empty property flagging as read only
+
+Example:
+
+nand: flash@0 {
+       compatible = "brcm,bcm2835-smi-nand";
+       status = "okay";
+
+       partition@0 {
+               label = "stage2";
+               // 128k
+               reg = <0 0x20000>;
+               read-only;
+       };
+       partition@1 {
+               label = "firmware";
+               // 16M
+               reg = <0x20000 0x1000000>;
+               read-only;
+       };
+       partition@2 {
+               label = "root";
+               // 2G
+               reg = <0x1020000 0x80000000>;
+       };
+};
\ No newline at end of file
index 1408d2ab89a68b2f4e8e432f79cf3003147e9280..437801780664e35b0b7511228327ba470094edba 100644 (file)
@@ -41,6 +41,13 @@ config MTD_SM_COMMON
        tristate
        default n
 
+config MTD_NAND_BCM2835_SMI
+        tristate "Use Broadcom's Secondary Memory Interface as a NAND controller (BCM283x)"
+        depends on BCM2835_SMI
+        default m
+        help
+         Uses the BCM2835's SMI peripheral as a NAND controller.
+
 config MTD_NAND_DENALI
        tristate
 
index 19a66e404d5ba949a16a7ba358df25f033e7a51c..3e2cecac083a6bdb340127233c10f40ce567814f 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI)         += denali.o
 obj-$(CONFIG_MTD_NAND_DENALI_PCI)      += denali_pci.o
 obj-$(CONFIG_MTD_NAND_DENALI_DT)       += denali_dt.o
 obj-$(CONFIG_MTD_NAND_AU1550)          += au1550nd.o
+obj-$(CONFIG_MTD_NAND_BCM2835_SMI)     += bcm2835_smi_nand.o
 obj-$(CONFIG_MTD_NAND_BF5XX)           += bf5xx_nand.o
 obj-$(CONFIG_MTD_NAND_S3C2410)         += s3c2410.o
 obj-$(CONFIG_MTD_NAND_TANGO)           += tango_nand.o
diff --git a/drivers/mtd/nand/bcm2835_smi_nand.c b/drivers/mtd/nand/bcm2835_smi_nand.c
new file mode 100644 (file)
index 0000000..02adda6
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * NAND flash driver for Broadcom Secondary Memory Interface
+ *
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/broadcom/bcm2835_smi.h>
+
+#define DEVICE_NAME "bcm2835-smi-nand"
+#define DRIVER_NAME "smi-nand-bcm2835"
+
+struct bcm2835_smi_nand_host {
+       struct bcm2835_smi_instance *smi_inst;
+       struct nand_chip nand_chip;
+       struct mtd_info mtd;
+       struct device *dev;
+};
+
+/****************************************************************************
+*
+*   NAND functionality implementation
+*
+****************************************************************************/
+
+#define SMI_NAND_CLE_PIN 0x01
+#define SMI_NAND_ALE_PIN 0x02
+
+static inline void bcm2835_smi_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+                                            unsigned int ctrl)
+{
+       uint32_t cmd32 = cmd;
+       uint32_t addr = ~(SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN);
+       struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
+       struct bcm2835_smi_instance *inst = host->smi_inst;
+
+       if (ctrl & NAND_CLE)
+               addr |= SMI_NAND_CLE_PIN;
+       if (ctrl & NAND_ALE)
+               addr |= SMI_NAND_ALE_PIN;
+       /* Lower ALL the CS pins! */
+       if (ctrl & NAND_NCE)
+               addr &= (SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN);
+
+       bcm2835_smi_set_address(inst, addr);
+
+       if (cmd != NAND_CMD_NONE)
+               bcm2835_smi_write_buf(inst, &cmd32, 1);
+}
+
+static inline uint8_t bcm2835_smi_nand_read_byte(struct mtd_info *mtd)
+{
+       uint8_t byte;
+       struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
+       struct bcm2835_smi_instance *inst = host->smi_inst;
+
+       bcm2835_smi_read_buf(inst, &byte, 1);
+       return byte;
+}
+
+static inline void bcm2835_smi_nand_write_byte(struct mtd_info *mtd,
+                                              uint8_t byte)
+{
+       struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
+       struct bcm2835_smi_instance *inst = host->smi_inst;
+
+       bcm2835_smi_write_buf(inst, &byte, 1);
+}
+
+static inline void bcm2835_smi_nand_write_buf(struct mtd_info *mtd,
+                                             const uint8_t *buf, int len)
+{
+       struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
+       struct bcm2835_smi_instance *inst = host->smi_inst;
+
+       bcm2835_smi_write_buf(inst, buf, len);
+}
+
+static inline void bcm2835_smi_nand_read_buf(struct mtd_info *mtd,
+                                            uint8_t *buf, int len)
+{
+       struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent);
+       struct bcm2835_smi_instance *inst = host->smi_inst;
+
+       bcm2835_smi_read_buf(inst, buf, len);
+}
+
+/****************************************************************************
+*
+*   Probe and remove functions
+*
+***************************************************************************/
+
+static int bcm2835_smi_nand_probe(struct platform_device *pdev)
+{
+       struct bcm2835_smi_nand_host *host;
+       struct nand_chip *this;
+       struct mtd_info *mtd;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node, *smi_node;
+       struct mtd_part_parser_data ppdata;
+       struct smi_settings *smi_settings;
+       struct bcm2835_smi_instance *smi_inst;
+       int ret = -ENXIO;
+
+       if (!node) {
+               dev_err(dev, "No device tree node supplied!");
+               return -EINVAL;
+       }
+
+       smi_node = of_parse_phandle(node, "smi_handle", 0);
+
+       /* Request use of SMI peripheral: */
+       smi_inst = bcm2835_smi_get(smi_node);
+
+       if (!smi_inst) {
+               dev_err(dev, "Could not register with SMI.");
+               return -EPROBE_DEFER;
+       }
+
+       /* Set SMI timing and bus width */
+
+       smi_settings = bcm2835_smi_get_settings_from_regs(smi_inst);
+
+       smi_settings->data_width = SMI_WIDTH_8BIT;
+       smi_settings->read_setup_time = 2;
+       smi_settings->read_hold_time = 1;
+       smi_settings->read_pace_time = 1;
+       smi_settings->read_strobe_time = 3;
+
+       smi_settings->write_setup_time = 2;
+       smi_settings->write_hold_time = 1;
+       smi_settings->write_pace_time = 1;
+       smi_settings->write_strobe_time = 3;
+
+       bcm2835_smi_set_regs_from_settings(smi_inst);
+
+       host = devm_kzalloc(dev, sizeof(struct bcm2835_smi_nand_host),
+               GFP_KERNEL);
+       if (!host)
+               return -ENOMEM;
+
+       host->dev = dev;
+       host->smi_inst = smi_inst;
+
+       platform_set_drvdata(pdev, host);
+
+       /* Link the structures together */
+
+       this = &host->nand_chip;
+       mtd = &host->mtd;
+       mtd->priv = this;
+       mtd->owner = THIS_MODULE;
+       mtd->dev.parent = dev;
+       mtd->name = DRIVER_NAME;
+
+       /* 20 us command delay time... */
+       this->chip_delay = 20;
+
+       this->priv = host;
+       this->cmd_ctrl = bcm2835_smi_nand_cmd_ctrl;
+       this->read_byte = bcm2835_smi_nand_read_byte;
+       this->write_byte = bcm2835_smi_nand_write_byte;
+       this->write_buf = bcm2835_smi_nand_write_buf;
+       this->read_buf = bcm2835_smi_nand_read_buf;
+
+       this->ecc.mode = NAND_ECC_SOFT;
+
+       /* Should never be accessed directly: */
+
+       this->IO_ADDR_R = (void *)0xdeadbeef;
+       this->IO_ADDR_W = (void *)0xdeadbeef;
+
+       /* First scan to find the device and get the page size */
+
+       if (nand_scan_ident(mtd, 1, NULL))
+               return -ENXIO;
+
+       /* Second phase scan */
+
+       if (nand_scan_tail(mtd))
+               return -ENXIO;
+
+       ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+       if (!ret)
+               return 0;
+
+       nand_release(mtd);
+       return -EINVAL;
+}
+
+static int bcm2835_smi_nand_remove(struct platform_device *pdev)
+{
+       struct bcm2835_smi_nand_host *host = platform_get_drvdata(pdev);
+
+       nand_release(&host->mtd);
+
+       return 0;
+}
+
+/****************************************************************************
+*
+*   Register the driver with device tree
+*
+***************************************************************************/
+
+static const struct of_device_id bcm2835_smi_nand_of_match[] = {
+       {.compatible = "brcm,bcm2835-smi-nand",},
+       { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, bcm2835_smi_nand_of_match);
+
+static struct platform_driver bcm2835_smi_nand_driver = {
+       .probe = bcm2835_smi_nand_probe,
+       .remove = bcm2835_smi_nand_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = bcm2835_smi_nand_of_match,
+       },
+};
+
+module_platform_driver(bcm2835_smi_nand_driver);
+
+MODULE_ALIAS("platform:smi-nand-bcm2835");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION
+       ("Driver for NAND chips using Broadcom Secondary Memory Interface");
+MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");