| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * MikroTik RB91x NAND flash driver
- *
- * Main part is copied from original driver written by Gabor Juhos.
- *
- * Copyright (C) 2013-2014 Gabor Juhos <[email protected]>
- */
- /*
- * 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/module.h>
- #include <linux/mtd/rawnand.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
- #include <linux/platform_device.h>
- #include <linux/gpio/consumer.h>
- #include <linux/version.h>
- #include <linux/of_platform.h>
- #include <asm/mach-ath79/ar71xx_regs.h>
- /* Bit masks for NAND data lines in ath79 gpio 32-bit register */
- #define RB91X_NAND_NRW_BIT BIT(12)
- #define RB91X_NAND_DATA_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) \
- | BIT(13) | BIT(14) | BIT(15))
- #define RB91X_NAND_LOW_DATA_MASK 0x1f
- #define RB91X_NAND_HIGH_DATA_MASK 0xe0
- #define RB91X_NAND_HIGH_DATA_SHIFT 8
- enum rb91x_nand_gpios {
- RB91X_NAND_READ,/* Read */
- RB91X_NAND_RDY, /* NAND Ready */
- RB91X_NAND_NCE, /* Chip Enable. Active low */
- RB91X_NAND_CLE, /* Command Latch Enable */
- RB91X_NAND_ALE, /* Address Latch Enable */
- RB91X_NAND_NRW, /* Read/Write. Active low */
- RB91X_NAND_NLE, /* Latch Enable. Active low */
- RB91X_NAND_PDIS, /* Reset Key Poll Disable. Active high */
- RB91X_NAND_GPIOS,
- };
- struct rb91x_nand_drvdata {
- struct nand_chip chip;
- struct device *dev;
- struct gpio_desc **gpio;
- void __iomem *ath79_gpio_base;
- };
- static inline void rb91x_nand_latch_lock(struct rb91x_nand_drvdata *drvdata,
- int lock)
- {
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_NLE], lock);
- }
- static inline void rb91x_nand_rst_key_poll_disable(struct rb91x_nand_drvdata *drvdata,
- int disable)
- {
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_PDIS], disable);
- }
- static int rb91x_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 rb91x_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 rb91x_nand_ecclayout_ops = {
- .ecc = rb91x_ooblayout_ecc,
- .free = rb91x_ooblayout_free,
- };
- static void rb91x_nand_write(struct rb91x_nand_drvdata *drvdata,
- const u8 *buf,
- unsigned len)
- {
- void __iomem *base = drvdata->ath79_gpio_base;
- u32 oe_reg;
- u32 out_reg;
- u32 out;
- unsigned i;
- rb91x_nand_latch_lock(drvdata, 1);
- rb91x_nand_rst_key_poll_disable(drvdata, 1);
- 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 & ~(RB91X_NAND_DATA_BITS | RB91X_NAND_NRW_BIT),
- base + AR71XX_GPIO_REG_OE);
- out = out_reg & ~(RB91X_NAND_DATA_BITS | RB91X_NAND_NRW_BIT);
- for (i = 0; i != len; i++) {
- u32 data;
- data = (buf[i] & RB91X_NAND_HIGH_DATA_MASK) <<
- RB91X_NAND_HIGH_DATA_SHIFT;
- data |= buf[i] & RB91X_NAND_LOW_DATA_MASK;
- data |= out;
- __raw_writel(data, base + AR71XX_GPIO_REG_OUT);
- /* Deactivate WE line */
- data |= RB91X_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);
- rb91x_nand_rst_key_poll_disable(drvdata, 0);
- rb91x_nand_latch_lock(drvdata, 0);
- }
- static void rb91x_nand_read(struct rb91x_nand_drvdata *drvdata,
- u8 *read_buf,
- unsigned len)
- {
- void __iomem *base = drvdata->ath79_gpio_base;
- u32 oe_reg;
- u32 out_reg;
- unsigned i;
- /* Enable read mode */
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_READ], 1);
- rb91x_nand_latch_lock(drvdata, 1);
- rb91x_nand_rst_key_poll_disable(drvdata, 1);
- /* 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 | RB91X_NAND_DATA_BITS,
- base + AR71XX_GPIO_REG_OE);
- for (i = 0; i < len; i++) {
- u32 in;
- u8 data;
- /* Activate RE line */
- __raw_writel(RB91X_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(RB91X_NAND_NRW_BIT, base + AR71XX_GPIO_REG_SET);
- data = (in & RB91X_NAND_LOW_DATA_MASK);
- data |= (in >> RB91X_NAND_HIGH_DATA_SHIFT) &
- RB91X_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);
- rb91x_nand_rst_key_poll_disable(drvdata, 0);
- rb91x_nand_latch_lock(drvdata, 0);
- /* Disable read mode */
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_READ], 0);
- }
- static int rb91x_nand_dev_ready(struct nand_chip *chip)
- {
- struct rb91x_nand_drvdata *drvdata = (struct rb91x_nand_drvdata *)(chip->priv);
- return gpiod_get_value_cansleep(drvdata->gpio[RB91X_NAND_RDY]);
- }
- static void rb91x_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
- unsigned int ctrl)
- {
- struct rb91x_nand_drvdata *drvdata = chip->priv;
- if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_CLE],
- (ctrl & NAND_CLE) ? 1 : 0);
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_ALE],
- (ctrl & NAND_ALE) ? 1 : 0);
- gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_NCE],
- (ctrl & NAND_NCE) ? 1 : 0);
- }
- if (cmd != NAND_CMD_NONE) {
- u8 t = cmd;
- rb91x_nand_write(drvdata, &t, 1);
- }
- }
- static u8 rb91x_nand_read_byte(struct nand_chip *chip)
- {
- u8 data = 0xff;
- rb91x_nand_read(chip->priv, &data, 1);
- return data;
- }
- static void rb91x_nand_read_buf(struct nand_chip *chip, u8 *buf, int len)
- {
- rb91x_nand_read(chip->priv, buf, len);
- }
- static void rb91x_nand_write_buf(struct nand_chip *chip, const u8 *buf, int len)
- {
- rb91x_nand_write(chip->priv, buf, len);
- }
- static void rb91x_nand_release(struct rb91x_nand_drvdata *drvdata)
- {
- mtd_device_unregister(nand_to_mtd(&drvdata->chip));
- nand_cleanup(&drvdata->chip);
- }
- static int rb91x_nand_probe(struct platform_device *pdev)
- {
- struct rb91x_nand_drvdata *drvdata;
- struct mtd_info *mtd;
- int r;
- struct device *dev = &pdev->dev;
- struct gpio_descs *gpios;
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
- platform_set_drvdata(pdev, drvdata);
- gpios = gpiod_get_array(dev, NULL, GPIOD_OUT_LOW);
- if (IS_ERR(gpios)) {
- if (PTR_ERR(gpios) != -EPROBE_DEFER) {
- dev_err(dev, "failed to get gpios: %d\n",
- PTR_ERR(gpios));
- }
- return PTR_ERR(gpios);
- }
- if (gpios->ndescs != RB91X_NAND_GPIOS) {
- dev_err(dev, "expected %d gpios\n", RB91X_NAND_GPIOS);
- return -EINVAL;
- }
- drvdata->gpio = gpios->desc;
- gpiod_direction_input(drvdata->gpio[RB91X_NAND_RDY]);
- drvdata->ath79_gpio_base = ioremap(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE);
- drvdata->dev = dev;
- drvdata->chip.priv = drvdata;
- drvdata->chip.legacy.cmd_ctrl = rb91x_nand_cmd_ctrl;
- drvdata->chip.legacy.dev_ready = rb91x_nand_dev_ready;
- drvdata->chip.legacy.read_byte = rb91x_nand_read_byte;
- drvdata->chip.legacy.write_buf = rb91x_nand_write_buf;
- drvdata->chip.legacy.read_buf = rb91x_nand_read_buf;
- drvdata->chip.legacy.chip_delay = 25;
- drvdata->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
- drvdata->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
- drvdata->chip.options = NAND_NO_SUBPAGE_WRITE;
- r = nand_scan(&drvdata->chip, 1);
- if (r) {
- dev_err(dev, "nand_scan() failed: %d\n", r);
- return r;
- }
- mtd = nand_to_mtd(&drvdata->chip);
- mtd->dev.parent = dev;
- mtd_set_of_node(mtd, dev->of_node);
- mtd->owner = THIS_MODULE;
- if (mtd->writesize == 512)
- mtd_set_ooblayout(mtd, &rb91x_nand_ecclayout_ops);
- r = mtd_device_register(mtd, NULL, 0);
- if (r) {
- dev_err(dev, "mtd_device_register() failed: %d\n",
- r);
- goto err_release_nand;
- }
- return 0;
- err_release_nand:
- rb91x_nand_release(drvdata);
- return r;
- }
- static int rb91x_nand_remove(struct platform_device *pdev)
- {
- struct rb91x_nand_drvdata *drvdata = platform_get_drvdata(pdev);
- rb91x_nand_release(drvdata);
- return 0;
- }
- static const struct of_device_id rb91x_nand_match[] = {
- { .compatible = "mikrotik,rb91x-nand" },
- {},
- };
- MODULE_DEVICE_TABLE(of, rb91x_nand_match);
- static struct platform_driver rb91x_nand_driver = {
- .probe = rb91x_nand_probe,
- .remove = rb91x_nand_remove,
- .driver = {
- .name = "rb91x-nand",
- .owner = THIS_MODULE,
- .of_match_table = rb91x_nand_match,
- },
- };
- module_platform_driver(rb91x_nand_driver);
- MODULE_DESCRIPTION("MikrotTik RB91x NAND flash driver");
- MODULE_VERSION(DRV_VERSION);
- MODULE_AUTHOR("Gabor Juhos <[email protected]>");
- MODULE_AUTHOR("Denis Kalashnikov <[email protected]>");
- MODULE_LICENSE("GPL v2");
|