Message ID | 20210506162514.5913-4-denis281089@gmail.com |
---|---|
State | Superseded |
Delegated to: | Koen Vandeputte |
Headers | show |
Series | RFC: ath79: add support for Mikrotik RB91xG | expand |
Hi, > -----Original Message----- > From: openwrt-devel [mailto:openwrt-devel-bounces@lists.openwrt.org] > On Behalf Of Denis Kalashnikov > Sent: Donnerstag, 6. Mai 2021 18:25 > To: openwrt-devel@lists.openwrt.org > Cc: Gabor Juhos <juhosg@openwrt.org> > Subject: [PATCH 3/4] ath79: add NAND driver for Mikrotik RB91xG > > Mikrotik guys doesn't use a NAND controller embedded in SoC on this board. > Instead they control NAND through SoC gpio lines. > Also NAND data and control lines are multiplexed by a latch. > More painful is that the latch lines that are not used for NAND control lines, > are used for two LEDs and a serial register nCS. > So, we need a MFD driver that control access to these shared lines. > > Signed-off-by: Denis Kalashnikov <denis281089@gmail.com> > --- > .../files/drivers/mtd/nand/raw/nand_rb91x.c | 432 ++++++++++++++++++ > target/linux/ath79/mikrotik/config-default | 1 + > .../patches-5.4/939-mikrotik-rb91x.patch | 21 + again, please include 5.10 here. > 3 files changed, 454 insertions(+) > create mode 100644 > target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c > > diff --git a/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c > b/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c > new file mode 100644 > index 0000000000..c9d5072547 > --- /dev/null > +++ b/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c > @@ -0,0 +1,432 @@ > +/* > + * NAND flash driver for the MikroTik RouterBOARD 91x series > + * > + * Main part is copied from original driver written by Gabor Juhos. > + * > + * Copyright (C) 2013-2014 Gabor Juhos <juhosg@openwrt.org> > + * > + * This program is free software; you can redistribute it and/or > +modify it > + * under the terms of the GNU General Public License version 2 as > +published > + * by the Free Software Foundation. > + */ If you create new files, please use SPDX license identifiers instead of this "long-but-incomplete" license plate. This of course also applies to the other patches. Best Adrian > + > +/* > + * WARNING: to speed up NAND reading/writing we are working with SoC > +GPIO > + * controller registers directly -- not through standard GPIO API. > + */ > + > +#include <linux/kernel.h> > +#include <linux/spinlock.h> > +#include <linux/module.h> > +#include <linux/mtd/rawnand.h> > +#include <linux/mtd/mtd.h> > +#include <linux/mtd/partitions.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/gpio.h> > +#include <linux/version.h> > + > +/* TODO: ar71xx regs (SoC GPIO controller ones) should be get from DTS? > +*/ #include <asm/mach-ath79/ar71xx_regs.h> //#include > +<asm/mach-ath79/ath79.h> > + > +#include <mfd/rb91x-ngl.h> > + > +#define DRIVER_NAME "rb91x-nand" > + > +static void __iomem *ath79_gpio_base; > + > +#define DRV_DESC "MikrotTik RB91x NAND flash driver" > + > +#define NAND_LOW_DATA_MASK 0x1f > +#define NAND_HIGH_DATA_MASK 0xe0 > +#define NAND_HIGH_DATA_SHIFT 8 > + > +struct drvdata { > + struct nand_chip chip; > + struct device *dev; > + struct rb91x_ngl *ngl; > + > + /* Bit masks */ > + u32 nand_rdy_bit; > + u32 nand_nrw_bit; > + u32 nand_data_bits; > + u32 nand_input_bits; > + u32 nand_output_bits; > +}; > + > +static inline struct drvdata *mtd_to_drvdata(struct mtd_info *mtd) { > + struct nand_chip *chip = mtd_to_nand(mtd); > + > + return container_of(chip, struct drvdata, chip); } > + > +static struct mtd_info *drvdata_to_mtd(struct drvdata *p) { > + return nand_to_mtd(&p->chip); > +} > + > +static int ooblayout_ecc(struct mtd_info *mtd, int section, > + struct mtd_oob_region *oobregion) > +{ > + switch (section) { > + case 0: > + oobregion->offset = 8; > + oobregion->length = 3; > + return 0; > + case 1: > + oobregion->offset = 13; > + oobregion->length = 3; > + return 0; > + default: > + return -ERANGE; > + } > +} > + > +static int ooblayout_free(struct mtd_info *mtd, int section, > + struct mtd_oob_region *oobregion) > +{ > + switch (section) { > + case 0: > + oobregion->offset = 0; > + oobregion->length = 4; > + return 0; > + case 1: > + oobregion->offset = 4; > + oobregion->length = 1; > + return 0; > + case 2: > + oobregion->offset = 6; > + oobregion->length = 2; > + return 0; > + case 3: > + oobregion->offset = 11; > + oobregion->length = 2; > + return 0; > + default: > + return -ERANGE; > + } > +} > + > +static const struct mtd_ooblayout_ops nand_ecclayout_ops = { > + .ecc = ooblayout_ecc, > + .free = ooblayout_free, > +}; > + > +/* TODO: Should be in DTS */ > +static struct mtd_partition nand_partitions[] = { > + { > + .name = "booter", > + .offset = 0, > + .size = (256 * 1024), > + .mask_flags = MTD_WRITEABLE, > + }, { > + .name = "kernel", > + .offset = (256 * 1024), > + .size = (4 * 1024 * 1024) - (256 * 1024), > + }, { > + .name = "ubi", > + .offset = MTDPART_OFS_NXTBLK, > + .size = MTDPART_SIZ_FULL, > + }, > +}; > + > +static void nand_write(struct drvdata *drvdata, > + const u8 *buf, > + unsigned len) > +{ > + void __iomem *base = ath79_gpio_base; > + u32 oe_reg; > + u32 out_reg; > + u32 out; > + unsigned i; > + struct rb91x_ngl *ngl = drvdata->ngl; > + > + ngl->latch_lock(ngl); > + > + oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE); > + out_reg = __raw_readl(base + AR71XX_GPIO_REG_OUT); > + > + /* set data lines to output mode */ > + __raw_writel(oe_reg & ~(drvdata->nand_data_bits | drvdata- > >nand_nrw_bit), > + base + AR71XX_GPIO_REG_OE); > + > + out = out_reg & ~(drvdata->nand_data_bits | drvdata- > >nand_nrw_bit); > + for (i = 0; i != len; i++) { > + u32 data; > + > + data = (buf[i] & NAND_HIGH_DATA_MASK) << > + NAND_HIGH_DATA_SHIFT; > + data |= buf[i] & NAND_LOW_DATA_MASK; > + data |= out; > + __raw_writel(data, base + AR71XX_GPIO_REG_OUT); > + > + /* deactivate WE line */ > + data |= drvdata->nand_nrw_bit; > + __raw_writel(data, base + AR71XX_GPIO_REG_OUT); > + /* flush write */ > + __raw_readl(base + AR71XX_GPIO_REG_OUT); > + } > + > + /* restore registers */ > + __raw_writel(out_reg, base + AR71XX_GPIO_REG_OUT); > + __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); > + /* flush write */ > + __raw_readl(base + AR71XX_GPIO_REG_OUT); > + > + ngl->latch_unlock(ngl); > +} > + > +static void nand_read(struct drvdata *drvdata, > + u8 *read_buf, > + unsigned len) > +{ > + void __iomem *base = ath79_gpio_base; > + u32 oe_reg; > + u32 out_reg; > + unsigned i; > + struct rb91x_ngl *ngl = drvdata->ngl; > + > + /* enable read mode */ > + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_READ, 1); > + > + ngl->latch_lock(ngl); > + > + /* save registers */ > + oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE); > + out_reg = __raw_readl(base + AR71XX_GPIO_REG_OUT); > + > + /* set data lines to input mode */ > + __raw_writel(oe_reg | drvdata->nand_data_bits, > + base + AR71XX_GPIO_REG_OE); > + > + for (i = 0; i < len; i++) { > + u32 in; > + u8 data; > + > + /* activate RE line */ > + __raw_writel(drvdata->nand_nrw_bit, base + > AR71XX_GPIO_REG_CLEAR); > + /* flush write */ > + __raw_readl(base + AR71XX_GPIO_REG_CLEAR); > + > + /* read input lines */ > + in = __raw_readl(base + AR71XX_GPIO_REG_IN); > + > + /* deactivate RE line */ > + __raw_writel(drvdata->nand_nrw_bit, base + > AR71XX_GPIO_REG_SET); > + > + data = (in & NAND_LOW_DATA_MASK); > + data |= (in >> NAND_HIGH_DATA_SHIFT) & > + NAND_HIGH_DATA_MASK; > + > + read_buf[i] = data; > + } > + > + /* restore registers */ > + __raw_writel(out_reg, base + AR71XX_GPIO_REG_OUT); > + __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); > + /* flush write */ > + __raw_readl(base + AR71XX_GPIO_REG_OUT); > + > + > + ngl->latch_unlock(ngl); > + > + /* disable read mode */ > + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_READ, 0); > + > +} > + > +static int dev_ready(struct nand_chip *chip) { > + struct rb91x_ngl *ngl = ((struct drvdata *)chip->priv)->ngl; > + > + return ngl->gpio_get_value(ngl, RB91X_NGL_NAND_RDY); } > + > +static void cmd_ctrl(struct nand_chip *chip, int cmd, > + unsigned int ctrl) > +{ > + struct drvdata *drvdata = chip->priv; > + struct rb91x_ngl *ngl = drvdata->ngl; > + > + if (ctrl & NAND_CTRL_CHANGE) { > + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_CLE, > + (ctrl & NAND_CLE) ? 1 : 0); > + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_ALE, > + (ctrl & NAND_ALE) ? 1 : 0); > + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_NCE, > + (ctrl & NAND_NCE) ? 0 : 1); > + } > + > + if (cmd != NAND_CMD_NONE) { > + u8 t = cmd; > + > + nand_write(drvdata, &t, 1); > + } > +} > + > +static u8 read_byte(struct nand_chip *chip) { > + u8 data = 0xff; > + > + nand_read(chip->priv, &data, 1); > + > + return data; > +} > + > +static void read_buf(struct nand_chip *chip, u8 *buf, int len) { > + nand_read(chip->priv, buf, len); > +} > + > +static void write_buf(struct nand_chip *chip, const u8 *buf, int len) { > + nand_write(chip->priv, buf, len); > +} > + > +static int probe(struct platform_device *pdev) { > + struct drvdata *drvdata; > + struct mtd_info *mtd; > + int ret, i; > + struct device *dev = &pdev->dev, *parent = dev->parent; > + struct rb91x_ngl *ngl; > + > + if (!parent) > + return -ENODEV; > + > + pr_info(DRV_DESC "\n"); > + > + /* TODO: Should be get from DTS */ > + ath79_gpio_base = ioremap(AR71XX_GPIO_BASE, > AR71XX_GPIO_SIZE); > + > + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), > GFP_KERNEL); > + if (!drvdata) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, drvdata); > + > + ngl = dev_get_drvdata(parent); > + drvdata->ngl = ngl; > + > + drvdata->dev = &pdev->dev; > + > + drvdata->chip.priv = drvdata; > + > + drvdata->chip.legacy.cmd_ctrl = cmd_ctrl; > + drvdata->chip.legacy.dev_ready = dev_ready; > + drvdata->chip.legacy.read_byte = read_byte; > + drvdata->chip.legacy.write_buf = write_buf; > + drvdata->chip.legacy.read_buf = read_buf; > + > + drvdata->chip.legacy.chip_delay = 25; > + drvdata->chip.ecc.mode = NAND_ECC_SOFT; > + drvdata->chip.ecc.algo = NAND_ECC_HAMMING; > + drvdata->chip.options = NAND_NO_SUBPAGE_WRITE; > + > + /* > + * WARNING: To speed up NAND reading/writing, we work with > + * SoC GPIO controller registers directly -- not through gpio API. > + * So, we suppose that data gpios and nRW and RDY gpios are > + * SoC gpios and its base is 0, another words: they are equal > + * to bits in the SoC GPIO controller registers. > + */ > + > + if (ngl->gpio[RB91X_NGL_NAND_RDY] >= 32) { > + dev_err(dev, "NAND RDY GPIO must be < 32\n"); > + return -EINVAL; > + } > + drvdata->nand_rdy_bit = BIT(ngl->gpio[RB91X_NGL_NAND_RDY]); > + > + if (ngl->gpio[RB91X_NGL_NAND_NRW] >= 32) { > + dev_err(dev, "NAND NRW GPIO must be < 32\n"); > + return -EINVAL; > + } > + drvdata->nand_nrw_bit = BIT(ngl->gpio[RB91X_NGL_NAND_NRW]); > + > + drvdata->nand_data_bits = 0; > + for (i = 0; i < ngl->nand_datas_count(ngl); i++) { > + if (ngl->gpio[RB91X_NGL_NAND_DATA0 + i] >= 32) { > + dev_err(dev, "NAND Data GPIO must be < 32\n"); > + return -EINVAL; > + } > + drvdata->nand_data_bits |= > + BIT(ngl->gpio[RB91X_NGL_NAND_DATA0 + i]); > + } > + > + drvdata->nand_input_bits = drvdata->nand_data_bits | > + drvdata->nand_rdy_bit; > + drvdata->nand_output_bits = drvdata->nand_data_bits | > + drvdata->nand_nrw_bit; > + > + /* Unlock latch */ > + ngl->gpio_direction_output(ngl, RB91X_NGL_NLE, 1); > + > + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_NCE, 1); > + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_NRW, 1); > + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_CLE, 0); > + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_ALE, 0); > + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_READ, 0); > + ngl->gpio_direction_input(ngl, RB91X_NGL_NAND_RDY); > + > + mtd = drvdata_to_mtd(drvdata); > + mtd->owner = THIS_MODULE; > + ret = nand_scan(&drvdata->chip, 1); > + if (ret) { > + dev_err(drvdata->dev, "nand_scan() failed: %d\n", ret); > + return ret; > + } > + > + if (mtd->writesize == 512) > + mtd_set_ooblayout(mtd, &nand_ecclayout_ops); > + > + ret = mtd_device_register(mtd, nand_partitions, > + ARRAY_SIZE(nand_partitions)); > + if (ret) { > + dev_err(drvdata->dev, "mtd_device_register() failed: %d\n", > + ret); > + goto err_release_nand; > + } > + > + return 0; > + > +err_release_nand: > + nand_release(&drvdata->chip); > + return ret; > +} > + > +static int remove(struct platform_device *pdev) { > + struct drvdata *drvdata = platform_get_drvdata(pdev); > + > + nand_release(&drvdata->chip); > + > + return 0; > +} > + > +static const struct of_device_id match[] = { > + { .compatible = "mikrotik,rb91x-nand" }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, match); > + > +static struct platform_driver rb91x_nand_driver = { > + .probe = probe, > + .remove = remove, > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + .of_match_table = match, > + }, > +}; > + > +module_platform_driver(rb91x_nand_driver); > + > +MODULE_DESCRIPTION(DRV_DESC); > +MODULE_VERSION(DRV_VERSION); > +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); > MODULE_AUTHOR("Denis > +Kalashnikov <denis281089@gmail.com>"); MODULE_LICENSE("GPL v2"); > diff --git a/target/linux/ath79/mikrotik/config-default > b/target/linux/ath79/mikrotik/config-default > index 92b7824eba..df5442d6b3 100644 > --- a/target/linux/ath79/mikrotik/config-default > +++ b/target/linux/ath79/mikrotik/config-default > @@ -17,6 +17,7 @@ CONFIG_MTD_NAND_AR934X=y > CONFIG_MTD_NAND_CORE=y CONFIG_MTD_NAND_ECC=y > CONFIG_MTD_NAND_RB4XX=y > +CONFIG_MTD_NAND_RB91X=y > CONFIG_MTD_RAW_NAND=y > CONFIG_MTD_ROUTERBOOT_PARTS=y > CONFIG_MTD_SPI_NAND=y > diff --git a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch > b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch > index f2135d486b..726dcdf973 100644 > --- a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch > +++ b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch > @@ -19,6 +19,27 @@ > obj-$(CONFIG_MFD_RB4XX_CPLD) += rb4xx-cpld.o > + > +obj-$(CONFIG_MFD_RB91X_NGL) += rb91x-ngl.o > +--- a/drivers/mtd/nand/raw/Kconfig > ++++ b/drivers/mtd/nand/raw/Kconfig > +@@ -559,4 +559,8 @@ config MTD_NAND_RB4XX > + Enables support for the NAND flash chip on Mikrotik Routerboard > + RB4xx series. > + > ++config MTD_NAND_RB91X > ++ tristate "Mikrotik RB91x NAND driver" > ++ depends on MFD_RB91X_NGL > ++ > + endif # MTD_RAW_NAND > +--- a/drivers/mtd/nand/raw/Makefile > ++++ b/drivers/mtd/nand/raw/Makefile > +@@ -59,6 +59,7 @@ obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm > + obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o > + obj-$(CONFIG_MTD_NAND_AR934X) += ar934x_nand.o > + obj-$(CONFIG_MTD_NAND_RB4XX) += nand_rb4xx.o > ++obj-$(CONFIG_MTD_NAND_RB91X) += nand_rb91x.o > + > + nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o > + nand_ids.o nand-objs += nand_onfi.o > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -130,6 +130,10 @@ config GPIO_ATH79 > -- > 2.26.3 > > > _______________________________________________ > openwrt-devel mailing list > openwrt-devel@lists.openwrt.org > https://lists.openwrt.org/mailman/listinfo/openwrt-devel
diff --git a/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c b/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c new file mode 100644 index 0000000000..c9d5072547 --- /dev/null +++ b/target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c @@ -0,0 +1,432 @@ +/* + * NAND flash driver for the MikroTik RouterBOARD 91x series + * + * Main part is copied from original driver written by Gabor Juhos. + * + * Copyright (C) 2013-2014 Gabor Juhos <juhosg@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +/* + * WARNING: to speed up NAND reading/writing we are working with SoC GPIO + * controller registers directly -- not through standard GPIO API. + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/mtd/rawnand.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/version.h> + +/* TODO: ar71xx regs (SoC GPIO controller ones) should be get from DTS? */ +#include <asm/mach-ath79/ar71xx_regs.h> +//#include <asm/mach-ath79/ath79.h> + +#include <mfd/rb91x-ngl.h> + +#define DRIVER_NAME "rb91x-nand" + +static void __iomem *ath79_gpio_base; + +#define DRV_DESC "MikrotTik RB91x NAND flash driver" + +#define NAND_LOW_DATA_MASK 0x1f +#define NAND_HIGH_DATA_MASK 0xe0 +#define NAND_HIGH_DATA_SHIFT 8 + +struct drvdata { + struct nand_chip chip; + struct device *dev; + struct rb91x_ngl *ngl; + + /* Bit masks */ + u32 nand_rdy_bit; + u32 nand_nrw_bit; + u32 nand_data_bits; + u32 nand_input_bits; + u32 nand_output_bits; +}; + +static inline struct drvdata *mtd_to_drvdata(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + return container_of(chip, struct drvdata, chip); +} + +static struct mtd_info *drvdata_to_mtd(struct drvdata *p) +{ + return nand_to_mtd(&p->chip); +} + +static int ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + oobregion->offset = 8; + oobregion->length = 3; + return 0; + case 1: + oobregion->offset = 13; + oobregion->length = 3; + return 0; + default: + return -ERANGE; + } +} + +static int ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + oobregion->offset = 0; + oobregion->length = 4; + return 0; + case 1: + oobregion->offset = 4; + oobregion->length = 1; + return 0; + case 2: + oobregion->offset = 6; + oobregion->length = 2; + return 0; + case 3: + oobregion->offset = 11; + oobregion->length = 2; + return 0; + default: + return -ERANGE; + } +} + +static const struct mtd_ooblayout_ops nand_ecclayout_ops = { + .ecc = ooblayout_ecc, + .free = ooblayout_free, +}; + +/* TODO: Should be in DTS */ +static struct mtd_partition nand_partitions[] = { + { + .name = "booter", + .offset = 0, + .size = (256 * 1024), + .mask_flags = MTD_WRITEABLE, + }, { + .name = "kernel", + .offset = (256 * 1024), + .size = (4 * 1024 * 1024) - (256 * 1024), + }, { + .name = "ubi", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, + }, +}; + +static void nand_write(struct drvdata *drvdata, + const u8 *buf, + unsigned len) +{ + void __iomem *base = ath79_gpio_base; + u32 oe_reg; + u32 out_reg; + u32 out; + unsigned i; + struct rb91x_ngl *ngl = drvdata->ngl; + + ngl->latch_lock(ngl); + + oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE); + out_reg = __raw_readl(base + AR71XX_GPIO_REG_OUT); + + /* set data lines to output mode */ + __raw_writel(oe_reg & ~(drvdata->nand_data_bits | drvdata->nand_nrw_bit), + base + AR71XX_GPIO_REG_OE); + + out = out_reg & ~(drvdata->nand_data_bits | drvdata->nand_nrw_bit); + for (i = 0; i != len; i++) { + u32 data; + + data = (buf[i] & NAND_HIGH_DATA_MASK) << + NAND_HIGH_DATA_SHIFT; + data |= buf[i] & NAND_LOW_DATA_MASK; + data |= out; + __raw_writel(data, base + AR71XX_GPIO_REG_OUT); + + /* deactivate WE line */ + data |= drvdata->nand_nrw_bit; + __raw_writel(data, base + AR71XX_GPIO_REG_OUT); + /* flush write */ + __raw_readl(base + AR71XX_GPIO_REG_OUT); + } + + /* restore registers */ + __raw_writel(out_reg, base + AR71XX_GPIO_REG_OUT); + __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); + /* flush write */ + __raw_readl(base + AR71XX_GPIO_REG_OUT); + + ngl->latch_unlock(ngl); +} + +static void nand_read(struct drvdata *drvdata, + u8 *read_buf, + unsigned len) +{ + void __iomem *base = ath79_gpio_base; + u32 oe_reg; + u32 out_reg; + unsigned i; + struct rb91x_ngl *ngl = drvdata->ngl; + + /* enable read mode */ + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_READ, 1); + + ngl->latch_lock(ngl); + + /* save registers */ + oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE); + out_reg = __raw_readl(base + AR71XX_GPIO_REG_OUT); + + /* set data lines to input mode */ + __raw_writel(oe_reg | drvdata->nand_data_bits, + base + AR71XX_GPIO_REG_OE); + + for (i = 0; i < len; i++) { + u32 in; + u8 data; + + /* activate RE line */ + __raw_writel(drvdata->nand_nrw_bit, base + AR71XX_GPIO_REG_CLEAR); + /* flush write */ + __raw_readl(base + AR71XX_GPIO_REG_CLEAR); + + /* read input lines */ + in = __raw_readl(base + AR71XX_GPIO_REG_IN); + + /* deactivate RE line */ + __raw_writel(drvdata->nand_nrw_bit, base + AR71XX_GPIO_REG_SET); + + data = (in & NAND_LOW_DATA_MASK); + data |= (in >> NAND_HIGH_DATA_SHIFT) & + NAND_HIGH_DATA_MASK; + + read_buf[i] = data; + } + + /* restore registers */ + __raw_writel(out_reg, base + AR71XX_GPIO_REG_OUT); + __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); + /* flush write */ + __raw_readl(base + AR71XX_GPIO_REG_OUT); + + + ngl->latch_unlock(ngl); + + /* disable read mode */ + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_READ, 0); + +} + +static int dev_ready(struct nand_chip *chip) +{ + struct rb91x_ngl *ngl = ((struct drvdata *)chip->priv)->ngl; + + return ngl->gpio_get_value(ngl, RB91X_NGL_NAND_RDY); +} + +static void cmd_ctrl(struct nand_chip *chip, int cmd, + unsigned int ctrl) +{ + struct drvdata *drvdata = chip->priv; + struct rb91x_ngl *ngl = drvdata->ngl; + + if (ctrl & NAND_CTRL_CHANGE) { + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_CLE, + (ctrl & NAND_CLE) ? 1 : 0); + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_ALE, + (ctrl & NAND_ALE) ? 1 : 0); + ngl->gpio_set_value(ngl, RB91X_NGL_NAND_NCE, + (ctrl & NAND_NCE) ? 0 : 1); + } + + if (cmd != NAND_CMD_NONE) { + u8 t = cmd; + + nand_write(drvdata, &t, 1); + } +} + +static u8 read_byte(struct nand_chip *chip) +{ + u8 data = 0xff; + + nand_read(chip->priv, &data, 1); + + return data; +} + +static void read_buf(struct nand_chip *chip, u8 *buf, int len) +{ + nand_read(chip->priv, buf, len); +} + +static void write_buf(struct nand_chip *chip, const u8 *buf, int len) +{ + nand_write(chip->priv, buf, len); +} + +static int probe(struct platform_device *pdev) +{ + struct drvdata *drvdata; + struct mtd_info *mtd; + int ret, i; + struct device *dev = &pdev->dev, *parent = dev->parent; + struct rb91x_ngl *ngl; + + if (!parent) + return -ENODEV; + + pr_info(DRV_DESC "\n"); + + /* TODO: Should be get from DTS */ + ath79_gpio_base = ioremap(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE); + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + platform_set_drvdata(pdev, drvdata); + + ngl = dev_get_drvdata(parent); + drvdata->ngl = ngl; + + drvdata->dev = &pdev->dev; + + drvdata->chip.priv = drvdata; + + drvdata->chip.legacy.cmd_ctrl = cmd_ctrl; + drvdata->chip.legacy.dev_ready = dev_ready; + drvdata->chip.legacy.read_byte = read_byte; + drvdata->chip.legacy.write_buf = write_buf; + drvdata->chip.legacy.read_buf = read_buf; + + drvdata->chip.legacy.chip_delay = 25; + drvdata->chip.ecc.mode = NAND_ECC_SOFT; + drvdata->chip.ecc.algo = NAND_ECC_HAMMING; + drvdata->chip.options = NAND_NO_SUBPAGE_WRITE; + + /* + * WARNING: To speed up NAND reading/writing, we work with + * SoC GPIO controller registers directly -- not through gpio API. + * So, we suppose that data gpios and nRW and RDY gpios are + * SoC gpios and its base is 0, another words: they are equal + * to bits in the SoC GPIO controller registers. + */ + + if (ngl->gpio[RB91X_NGL_NAND_RDY] >= 32) { + dev_err(dev, "NAND RDY GPIO must be < 32\n"); + return -EINVAL; + } + drvdata->nand_rdy_bit = BIT(ngl->gpio[RB91X_NGL_NAND_RDY]); + + if (ngl->gpio[RB91X_NGL_NAND_NRW] >= 32) { + dev_err(dev, "NAND NRW GPIO must be < 32\n"); + return -EINVAL; + } + drvdata->nand_nrw_bit = BIT(ngl->gpio[RB91X_NGL_NAND_NRW]); + + drvdata->nand_data_bits = 0; + for (i = 0; i < ngl->nand_datas_count(ngl); i++) { + if (ngl->gpio[RB91X_NGL_NAND_DATA0 + i] >= 32) { + dev_err(dev, "NAND Data GPIO must be < 32\n"); + return -EINVAL; + } + drvdata->nand_data_bits |= + BIT(ngl->gpio[RB91X_NGL_NAND_DATA0 + i]); + } + + drvdata->nand_input_bits = drvdata->nand_data_bits | + drvdata->nand_rdy_bit; + drvdata->nand_output_bits = drvdata->nand_data_bits | + drvdata->nand_nrw_bit; + + /* Unlock latch */ + ngl->gpio_direction_output(ngl, RB91X_NGL_NLE, 1); + + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_NCE, 1); + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_NRW, 1); + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_CLE, 0); + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_ALE, 0); + ngl->gpio_direction_output(ngl, RB91X_NGL_NAND_READ, 0); + ngl->gpio_direction_input(ngl, RB91X_NGL_NAND_RDY); + + mtd = drvdata_to_mtd(drvdata); + mtd->owner = THIS_MODULE; + ret = nand_scan(&drvdata->chip, 1); + if (ret) { + dev_err(drvdata->dev, "nand_scan() failed: %d\n", ret); + return ret; + } + + if (mtd->writesize == 512) + mtd_set_ooblayout(mtd, &nand_ecclayout_ops); + + ret = mtd_device_register(mtd, nand_partitions, + ARRAY_SIZE(nand_partitions)); + if (ret) { + dev_err(drvdata->dev, "mtd_device_register() failed: %d\n", + ret); + goto err_release_nand; + } + + return 0; + +err_release_nand: + nand_release(&drvdata->chip); + return ret; +} + +static int remove(struct platform_device *pdev) +{ + struct drvdata *drvdata = platform_get_drvdata(pdev); + + nand_release(&drvdata->chip); + + return 0; +} + +static const struct of_device_id match[] = { + { .compatible = "mikrotik,rb91x-nand" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, match); + +static struct platform_driver rb91x_nand_driver = { + .probe = probe, + .remove = remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = match, + }, +}; + +module_platform_driver(rb91x_nand_driver); + +MODULE_DESCRIPTION(DRV_DESC); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); +MODULE_AUTHOR("Denis Kalashnikov <denis281089@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ath79/mikrotik/config-default b/target/linux/ath79/mikrotik/config-default index 92b7824eba..df5442d6b3 100644 --- a/target/linux/ath79/mikrotik/config-default +++ b/target/linux/ath79/mikrotik/config-default @@ -17,6 +17,7 @@ CONFIG_MTD_NAND_AR934X=y CONFIG_MTD_NAND_CORE=y CONFIG_MTD_NAND_ECC=y CONFIG_MTD_NAND_RB4XX=y +CONFIG_MTD_NAND_RB91X=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_ROUTERBOOT_PARTS=y CONFIG_MTD_SPI_NAND=y diff --git a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch index f2135d486b..726dcdf973 100644 --- a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch +++ b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch @@ -19,6 +19,27 @@ obj-$(CONFIG_MFD_RB4XX_CPLD) += rb4xx-cpld.o + +obj-$(CONFIG_MFD_RB91X_NGL) += rb91x-ngl.o +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -559,4 +559,8 @@ config MTD_NAND_RB4XX + Enables support for the NAND flash chip on Mikrotik Routerboard + RB4xx series. + ++config MTD_NAND_RB91X ++ tristate "Mikrotik RB91x NAND driver" ++ depends on MFD_RB91X_NGL ++ + endif # MTD_RAW_NAND +--- a/drivers/mtd/nand/raw/Makefile ++++ b/drivers/mtd/nand/raw/Makefile +@@ -59,6 +59,7 @@ obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm + obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o + obj-$(CONFIG_MTD_NAND_AR934X) += ar934x_nand.o + obj-$(CONFIG_MTD_NAND_RB4XX) += nand_rb4xx.o ++obj-$(CONFIG_MTD_NAND_RB91X) += nand_rb91x.o + + nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o + nand-objs += nand_onfi.o --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -130,6 +130,10 @@ config GPIO_ATH79
Mikrotik guys doesn't use a NAND controller embedded in SoC on this board. Instead they control NAND through SoC gpio lines. Also NAND data and control lines are multiplexed by a latch. More painful is that the latch lines that are not used for NAND control lines, are used for two LEDs and a serial register nCS. So, we need a MFD driver that control access to these shared lines. Signed-off-by: Denis Kalashnikov <denis281089@gmail.com> --- .../files/drivers/mtd/nand/raw/nand_rb91x.c | 432 ++++++++++++++++++ target/linux/ath79/mikrotik/config-default | 1 + .../patches-5.4/939-mikrotik-rb91x.patch | 21 + 3 files changed, 454 insertions(+) create mode 100644 target/linux/ath79/files/drivers/mtd/nand/raw/nand_rb91x.c