[PATCH 09/10] mtd: devices: Add driver for memory mapped NVRAM on FPC

From: Pantelis Antoniou
Date: Fri Oct 07 2016 - 11:42:42 EST


From: JawaharBalaji Thirumalaisamy <jawaharb@xxxxxxxxxxx>

Add MTD driver for NVRAM on Juniper Gladiator FPC PMB. This driver uses
indirect memory-mapped access facilitated by bootcpld. Requires
cpld_ver >= 0XC6 and DT support.

Signed-off-by: Georgi Vlaev <gvlaev@xxxxxxxxxxx>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@xxxxxxxxxxx>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@xxxxxxxxxxxx>
---
drivers/mtd/devices/Kconfig | 11 +++
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/jnx_pmb_nvram.c | 191 ++++++++++++++++++++++++++++++++++++
3 files changed, 203 insertions(+)
create mode 100644 drivers/mtd/devices/jnx_pmb_nvram.c

diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 58329d2..d4255fb 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -144,6 +144,17 @@ config MTD_LART
not need any mapping/chip driver for LART. This one does it all
for you, so go disable all of those if you enabled some of them (:

+config JNX_PMB_NVRAM
+ tristate "Juniper FPC PMB NVRAM Driver"
+ depends on (PTXPMB_COMMON || JNX_PTX_NGPMB)
+ default y if (PTXPMB_COMMON || JNX_PTX_NGPMB)
+ help
+ This driver adds support for NVRAM on Gladiator 3T FPC which is connected
+ to the BOOTCPLD (cpld_version >= C6).
+
+ This driver can also be built as a module. When it is so the name of
+ the module is ngpmb-nvram.
+
config MTD_MTDRAM
tristate "Test driver using RAM"
help
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 7912d3a..b407c5fc 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o

+obj-$(CONFIG_JNX_PMB_NVRAM) += jnx_pmb_nvram.o

CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/jnx_pmb_nvram.c b/drivers/mtd/devices/jnx_pmb_nvram.c
new file mode 100644
index 0000000..8a1e812
--- /dev/null
+++ b/drivers/mtd/devices/jnx_pmb_nvram.c
@@ -0,0 +1,191 @@
+/*
+ * Juniper Networks PTX1K RCB I2CS Boot FPGA MTD driver
+ * FPGA upgrades of the Spartan3AN/XC3S700 based I2CS.
+ *
+ * Copyright (C) 2015 Juniper Networks. All rights reserved.
+ * Author: JawaharBalaji Thirumalaisamy <jawaharb@xxxxxxxxxxx>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mfd/ptxpmb_cpld.h>
+
+struct nvram_mtd {
+ void __iomem *base;
+ struct device *dev;
+ struct mtd_info mtd;
+ struct mutex lock;
+};
+
+
+static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct nvram_mtd *nvram = container_of(mtd, struct nvram_mtd, mtd);
+
+ memset((char *)nvram->base + instr->addr, 0xff, instr->len);
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+ return 0;
+}
+
+static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, void **virt, resource_size_t *phys)
+{
+ struct nvram_mtd *nvram = container_of(mtd, struct nvram_mtd, mtd);
+
+ virt = nvram->base + from;
+ *retlen = len;
+ return 0;
+}
+
+static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ return 0;
+}
+
+static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct nvram_mtd *nvram = container_of(mtd, struct nvram_mtd, mtd);
+
+ memcpy(buf, nvram->base + from, len);
+ *retlen = len;
+ return 0;
+}
+
+static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct nvram_mtd *nvram = container_of(mtd, struct nvram_mtd, mtd);
+
+ memcpy((char *)nvram->base + to, buf, len);
+ *retlen = len;
+ return 0;
+}
+
+int nvram_init_mtd_parse(struct platform_device *pdev, struct mtd_info *mtd)
+{
+ struct mtd_part_parser_data ppdata = {};
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ mtd->name = dev_name(dev);
+ mtd->type = MTD_RAM;
+ mtd->flags = MTD_CAP_RAM;
+ mtd->size = 0xFF00;
+ mtd->writesize = 1;
+ mtd->writebufsize = 64; /* Mimic CFI NOR flashes */
+ mtd->erasesize = 0x1000;
+ mtd->owner = THIS_MODULE;
+ mtd->_erase = ram_erase;
+ mtd->_point = ram_point;
+ mtd->_unpoint = ram_unpoint;
+ mtd->_read = ram_read;
+ mtd->_write = ram_write;
+ mtd->_panic_write = ram_write;
+
+ ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ if (ret) {
+ dev_err(dev, "mtd_device_parse_register returned %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int nvram_probe(struct platform_device *pdev)
+{
+ struct pmb_boot_cpld __iomem *cpld;
+ struct nvram_mtd *nvram;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ nvram = devm_kzalloc(dev, sizeof(*nvram), GFP_KERNEL);
+ if (!nvram)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Failed to get nvram mmio resource\n");
+ return -ENOENT;
+ }
+ nvram->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!nvram->base) {
+ dev_err(dev, "Cannot map nvram\n");
+ return -EADDRNOTAVAIL;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ /* Always assume that we need cpld control */
+ dev_err(dev, "Failed to get cpld mmio resource\n");
+ return -ENOENT;
+ }
+ cpld = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!cpld) {
+ dev_err(dev, "Cannot map cpld\n");
+ return -EADDRNOTAVAIL;
+ }
+ nvram->dev = dev;
+ platform_set_drvdata(pdev, nvram);
+ ret = nvram_init_mtd_parse(pdev, &nvram->mtd);
+ if (ret)
+ return ret;
+
+ if (READ_ONCE(cpld->cpld_rev) < 0xC6)
+ dev_info(dev, "NVRAM requires atleast cpld_rev 0XC6\n");
+
+ /* Initialize the window register in the cpld*/
+ WRITE_ONCE(cpld->board.nvram.nv_win, 0x0);
+ dev_info(dev, "Initialized window:0x%x\n",
+ READ_ONCE(cpld->board.nvram.nv_win));
+ return ret;
+}
+
+static int nvram_remove(struct platform_device *pdev)
+{
+ struct nvram_mtd *nvram;
+
+ nvram = platform_get_drvdata(pdev);
+ mtd_device_unregister(&nvram->mtd);
+ return 0;
+}
+
+static const struct of_device_id ngpmb_mtd_ids[] = {
+ { .compatible = "jnx,ngpmb-nvram", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ngpmb_mtd_ids);
+
+static struct platform_driver nvram_driver = {
+ .probe = nvram_probe,
+ .remove = nvram_remove,
+ .driver = {
+ .name = "ngpmb-nvram",
+ .owner = THIS_MODULE,
+ .of_match_table = ngpmb_mtd_ids,
+ },
+};
+
+module_platform_driver(nvram_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("JawaharBalaji Thirumalaisamy <jawaharb@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("EVO PTXPMB CPLD NVRAM Driver");
+MODULE_ALIAS("platform:ngpmb-nvram");
--
1.9.1