openwrtv3/target/linux/ar71xx/files/drivers/mtd/nand/rb91x_nand.c
Felix Fietkau 9db9072d67 ar71xx: convert mikrotik routerboard support to UBI
Remove the wget2nand script, drop the need for manual installation,
use sysupgrade instead.

There are now two different NAND images, one for 64 MiB flashes, the
other for >= 128 MiB

Signed-off-by: Felix Fietkau <nbd@nbd.name>
2017-01-16 20:40:12 +01:00

378 lines
8.9 KiB
C

/*
* NAND flash driver for the MikroTik RouterBOARD 91x series
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/mtd/nand.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/platform_data/rb91x_nand.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include <asm/mach-ath79/ath79.h>
#define DRV_DESC "NAND flash driver for the RouterBOARD 91x series"
#define RB91X_NAND_NRWE 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_INPUT_BITS (RB91X_NAND_DATA_BITS | RB91X_NAND_RDY)
#define RB91X_NAND_OUTPUT_BITS (RB91X_NAND_DATA_BITS | RB91X_NAND_NRWE)
#define RB91X_NAND_LOW_DATA_MASK 0x1f
#define RB91X_NAND_HIGH_DATA_MASK 0xe0
#define RB91X_NAND_HIGH_DATA_SHIFT 8
struct rb91x_nand_info {
struct nand_chip chip;
struct mtd_info mtd;
struct device *dev;
int gpio_nce;
int gpio_ale;
int gpio_cle;
int gpio_rdy;
int gpio_read;
int gpio_nrw;
int gpio_nle;
};
static inline struct rb91x_nand_info *mtd_to_rbinfo(struct mtd_info *mtd)
{
return container_of(mtd, struct rb91x_nand_info, mtd);
}
/*
* We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader
* will not be able to find the kernel that we load.
*/
static struct nand_ecclayout rb91x_nand_ecclayout = {
.eccbytes = 6,
.eccpos = { 8, 9, 10, 13, 14, 15 },
.oobavail = 9,
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
};
static struct mtd_partition rb91x_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 rb91x_nand_write(struct rb91x_nand_info *rbni,
const u8 *buf,
unsigned len)
{
void __iomem *base = ath79_gpio_base;
u32 oe_reg;
u32 out_reg;
u32 out;
unsigned i;
/* enable the latch */
gpio_set_value_cansleep(rbni->gpio_nle, 0);
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_NRWE),
base + AR71XX_GPIO_REG_OE);
out = out_reg & ~(RB91X_NAND_DATA_BITS | RB91X_NAND_NRWE);
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_NRWE;
__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);
/* disable the latch */
gpio_set_value_cansleep(rbni->gpio_nle, 1);
}
static void rb91x_nand_read(struct rb91x_nand_info *rbni,
u8 *read_buf,
unsigned len)
{
void __iomem *base = ath79_gpio_base;
u32 oe_reg;
u32 out_reg;
unsigned i;
/* enable read mode */
gpio_set_value_cansleep(rbni->gpio_read, 1);
/* enable latch */
gpio_set_value_cansleep(rbni->gpio_nle, 0);
/* 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_NRWE, 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_NRWE, 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);
/* disable latch */
gpio_set_value_cansleep(rbni->gpio_nle, 1);
/* disable read mode */
gpio_set_value_cansleep(rbni->gpio_read, 0);
}
static int rb91x_nand_dev_ready(struct mtd_info *mtd)
{
struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd);
return gpio_get_value_cansleep(rbni->gpio_rdy);
}
static void rb91x_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd);
if (ctrl & NAND_CTRL_CHANGE) {
gpio_set_value_cansleep(rbni->gpio_cle,
(ctrl & NAND_CLE) ? 1 : 0);
gpio_set_value_cansleep(rbni->gpio_ale,
(ctrl & NAND_ALE) ? 1 : 0);
gpio_set_value_cansleep(rbni->gpio_nce,
(ctrl & NAND_NCE) ? 0 : 1);
}
if (cmd != NAND_CMD_NONE) {
u8 t = cmd;
rb91x_nand_write(rbni, &t, 1);
}
}
static u8 rb91x_nand_read_byte(struct mtd_info *mtd)
{
struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd);
u8 data = 0xff;
rb91x_nand_read(rbni, &data, 1);
return data;
}
static void rb91x_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd);
rb91x_nand_read(rbni, buf, len);
}
static void rb91x_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd);
rb91x_nand_write(rbni, buf, len);
}
static int rb91x_nand_gpio_init(struct rb91x_nand_info *info)
{
int ret;
/*
* Ensure that the LATCH is disabled before initializing
* control lines.
*/
ret = devm_gpio_request_one(info->dev, info->gpio_nle,
GPIOF_OUT_INIT_HIGH, "LATCH enable");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_nce,
GPIOF_OUT_INIT_HIGH, "NAND nCE");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_nrw,
GPIOF_OUT_INIT_HIGH, "NAND nRW");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_cle,
GPIOF_OUT_INIT_LOW, "NAND CLE");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_ale,
GPIOF_OUT_INIT_LOW, "NAND ALE");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_read,
GPIOF_OUT_INIT_LOW, "NAND READ");
if (ret)
return ret;
ret = devm_gpio_request_one(info->dev, info->gpio_rdy,
GPIOF_IN, "NAND RDY");
return ret;
}
static int rb91x_nand_probe(struct platform_device *pdev)
{
struct rb91x_nand_info *rbni;
struct rb91x_nand_platform_data *pdata;
int ret;
pr_info(DRV_DESC "\n");
pdata = dev_get_platdata(&pdev->dev);
if (!pdata)
return -EINVAL;
rbni = devm_kzalloc(&pdev->dev, sizeof(*rbni), GFP_KERNEL);
if (!rbni)
return -ENOMEM;
rbni->dev = &pdev->dev;
rbni->gpio_nce = pdata->gpio_nce;
rbni->gpio_ale = pdata->gpio_ale;
rbni->gpio_cle = pdata->gpio_cle;
rbni->gpio_read = pdata->gpio_read;
rbni->gpio_nrw = pdata->gpio_nrw;
rbni->gpio_rdy = pdata->gpio_rdy;
rbni->gpio_nle = pdata->gpio_nle;
rbni->chip.priv = &rbni;
rbni->mtd.priv = &rbni->chip;
rbni->mtd.owner = THIS_MODULE;
rbni->chip.cmd_ctrl = rb91x_nand_cmd_ctrl;
rbni->chip.dev_ready = rb91x_nand_dev_ready;
rbni->chip.read_byte = rb91x_nand_read_byte;
rbni->chip.write_buf = rb91x_nand_write_buf;
rbni->chip.read_buf = rb91x_nand_read_buf;
rbni->chip.chip_delay = 25;
rbni->chip.ecc.mode = NAND_ECC_SOFT;
rbni->chip.options = NAND_NO_SUBPAGE_WRITE;
platform_set_drvdata(pdev, rbni);
ret = rb91x_nand_gpio_init(rbni);
if (ret)
return ret;
ret = nand_scan_ident(&rbni->mtd, 1, NULL);
if (ret)
return ret;
if (rbni->mtd.writesize == 512)
rbni->chip.ecc.layout = &rb91x_nand_ecclayout;
ret = nand_scan_tail(&rbni->mtd);
if (ret)
return ret;
ret = mtd_device_register(&rbni->mtd, rb91x_nand_partitions,
ARRAY_SIZE(rb91x_nand_partitions));
if (ret)
goto err_release_nand;
return 0;
err_release_nand:
nand_release(&rbni->mtd);
return ret;
}
static int rb91x_nand_remove(struct platform_device *pdev)
{
struct rb91x_nand_info *info = platform_get_drvdata(pdev);
nand_release(&info->mtd);
return 0;
}
static struct platform_driver rb91x_nand_driver = {
.probe = rb91x_nand_probe,
.remove = rb91x_nand_remove,
.driver = {
.name = RB91X_NAND_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
module_platform_driver(rb91x_nand_driver);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_LICENSE("GPL v2");