358 lines
11 KiB
Diff
358 lines
11 KiB
Diff
|
From 83148030b231764e7a5fc14bd2c9ccc6a104a872 Mon Sep 17 00:00:00 2001
|
||
|
From: Luke Wren <wren6991@gmail.com>
|
||
|
Date: Sat, 5 Sep 2015 01:16:10 +0100
|
||
|
Subject: [PATCH] Add SMI NAND driver
|
||
|
|
||
|
Signed-off-by: Luke Wren <wren6991@gmail.com>
|
||
|
---
|
||
|
.../bindings/mtd/brcm,bcm2835-smi-nand.txt | 42 ++++
|
||
|
drivers/mtd/nand/Kconfig | 7 +
|
||
|
drivers/mtd/nand/Makefile | 1 +
|
||
|
drivers/mtd/nand/bcm2835_smi_nand.c | 267 +++++++++++++++++++++
|
||
|
4 files changed, 317 insertions(+)
|
||
|
create mode 100644 Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt
|
||
|
create mode 100644 drivers/mtd/nand/bcm2835_smi_nand.c
|
||
|
|
||
|
--- /dev/null
|
||
|
+++ b/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt
|
||
|
@@ -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
|
||
|
--- a/drivers/mtd/nand/Kconfig
|
||
|
+++ b/drivers/mtd/nand/Kconfig
|
||
|
@@ -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
|
||
|
|
||
|
--- a/drivers/mtd/nand/Makefile
|
||
|
+++ b/drivers/mtd/nand/Makefile
|
||
|
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali
|
||
|
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_DAVINCI) += davinci_nand.o
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/mtd/nand/bcm2835_smi_nand.c
|
||
|
@@ -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>");
|