diff options
3 files changed, 174 insertions, 115 deletions
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c index 1da447b406..cb9abf7627 100644 --- a/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c +++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c @@ -23,6 +23,7 @@ #include <linux/routerboot.h> #include <linux/gpio.h> #include <linux/platform_data/gpio-latch.h> +#include <linux/platform_data/rb91x_nand.h> #include <asm/prom.h> #include <asm/mach-ath79/ath79.h> @@ -54,6 +55,14 @@ #define RB91X_LATCH_GPIO_BASE AR934X_GPIO_COUNT #define RB91X_LATCH_GPIO(_x) (RB91X_LATCH_GPIO_BASE + (_x)) +#define RB91X_GPIO_NAND_READ RB91X_LATCH_GPIO(3) +#define RB91X_GPIO_NAND_RDY RB91X_LATCH_GPIO(4) +#define RB91X_GPIO_NLE RB91X_LATCH_GPIO(11) +#define RB91X_GPIO_NAND_NRW RB91X_LATCH_GPIO(12) +#define RB91X_GPIO_NAND_NCE RB91X_LATCH_GPIO(13) +#define RB91X_GPIO_NAND_CLE RB91X_LATCH_GPIO(14) +#define RB91X_GPIO_NAND_ALE RB91X_LATCH_GPIO(15) + struct rb_board_info { const char *name; u32 flags; @@ -97,6 +106,16 @@ static struct gpio_latch_platform_data rb711gr100_gpio_latch_data __initdata = { .le_active_low = true, }; +static struct rb91x_nand_platform_data rb711gr100_nand_data __initdata = { + .gpio_nce = RB91X_GPIO_NAND_NCE, + .gpio_ale = RB91X_GPIO_NAND_ALE, + .gpio_cle = RB91X_GPIO_NAND_CLE, + .gpio_rdy = RB91X_GPIO_NAND_RDY, + .gpio_read = RB91X_GPIO_NAND_READ, + .gpio_nrw = RB91X_GPIO_NAND_NRW, + .gpio_nle = RB91X_GPIO_NLE, +}; + static void __init rb711gr100_init_partitions(const struct rb_info *info) { rb711gr100_spi_partitions[0].size = info->hard_cfg_offs; @@ -179,7 +198,9 @@ static void __init rb711gr100_setup(void) rb711gr100_wlan_init(); - platform_device_register_simple("rb91x-nand", -1, NULL, 0); + platform_device_register_data(NULL, "rb91x-nand", -1, + &rb711gr100_nand_data, + sizeof(rb711gr100_nand_data)); platform_device_register_data(NULL, "gpio-latch", -1, &rb711gr100_gpio_latch_data, diff --git a/target/linux/ar71xx/files/drivers/mtd/nand/rb91x_nand.c b/target/linux/ar71xx/files/drivers/mtd/nand/rb91x_nand.c index 99828cb34c..f0aa3c3e79 100644 --- a/target/linux/ar71xx/files/drivers/mtd/nand/rb91x_nand.c +++ b/target/linux/ar71xx/files/drivers/mtd/nand/rb91x_nand.c @@ -1,7 +1,7 @@ /* * NAND flash driver for the MikroTik RouterBOARD 91x series * - * Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org> + * 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 @@ -17,35 +17,38 @@ #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_NAME "rb91x-nand" #define DRV_DESC "NAND flash driver for the RouterBOARD 91x series" -#define RB91X_NAND_NRE_ENABLE BIT(3) -#define RB91X_NAND_RDY BIT(4) -#define RB91X_LATCH_ENABLE BIT(11) #define RB91X_NAND_NRWE BIT(12) -#define RB91X_NAND_NCE BIT(13) -#define RB91X_NAND_CLE BIT(14) -#define RB91X_NAND_ALE BIT(15) #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_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 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) @@ -81,53 +84,9 @@ static struct mtd_partition rb91x_nand_partitions[] = { }, }; -static void rb91x_change_gpo(u32 clear, u32 set) -{ - void __iomem *base = ath79_gpio_base; - static unsigned on = 0xE002800; - static unsigned off = 0x0000C008; - static unsigned oe = 0; - static DEFINE_SPINLOCK(lock); - unsigned long flags; - - spin_lock_irqsave(&lock, flags); - - on = (on | set) & ~clear; - off = (off | clear) & ~set; - - if (!oe) - oe = __raw_readl(base + AR71XX_GPIO_REG_OE); - - if (on & RB91X_LATCH_ENABLE) { - u32 t; - - t = oe & __raw_readl(base + AR71XX_GPIO_REG_OE); - t &= ~(on | off); - __raw_writel(t, base + AR71XX_GPIO_REG_OE); - __raw_writel(off, base + AR71XX_GPIO_REG_CLEAR); - __raw_writel(on, base + AR71XX_GPIO_REG_SET); - } else if (clear & RB91X_LATCH_ENABLE) { - oe = __raw_readl(base + AR71XX_GPIO_REG_OE); - __raw_writel(RB91X_LATCH_ENABLE, - base + AR71XX_GPIO_REG_CLEAR); - /* flush write */ - __raw_readl(base + AR71XX_GPIO_REG_CLEAR); - } - - spin_unlock_irqrestore(&lock, flags); -} - -static inline void rb91x_latch_enable(void) -{ - rb91x_change_gpo(RB91X_LATCH_ENABLE, 0); -} - -static inline void rb91x_latch_disable(void) -{ - rb91x_change_gpo(0, RB91X_LATCH_ENABLE); -} - -static void rb91x_nand_write(const u8 *buf, unsigned len) +static void rb91x_nand_write(struct rb91x_nand_info *rbni, + const u8 *buf, + unsigned len) { void __iomem *base = ath79_gpio_base; u32 oe_reg; @@ -135,7 +94,8 @@ static void rb91x_nand_write(const u8 *buf, unsigned len) u32 out; unsigned i; - rb91x_latch_enable(); + /* 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); @@ -162,30 +122,32 @@ static void rb91x_nand_write(const u8 *buf, unsigned len) } /* restore registers */ - __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); __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_latch_disable(); + /* disable the latch */ + gpio_set_value_cansleep(rbni->gpio_nle, 1); } -static void rb91x_nand_read(u8 *read_buf, unsigned len) +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; - /* save registers */ - oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE); - - /* select nRE mode */ - rb91x_change_gpo(0, RB91X_NAND_NRE_ENABLE); + /* enable read mode */ + gpio_set_value_cansleep(rbni->gpio_read, 1); /* enable latch */ - rb91x_latch_enable(); + 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 */ @@ -215,109 +177,169 @@ static void rb91x_nand_read(u8 *read_buf, unsigned len) } /* restore registers */ - __raw_writel(oe_reg, base + AR71XX_GPIO_REG_OE); __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 */ - rb91x_latch_disable(); + gpio_set_value_cansleep(rbni->gpio_nle, 1); - /* deselect nRE mode */ - rb91x_change_gpo(RB91X_NAND_NRE_ENABLE, 0); + /* disable read mode */ + gpio_set_value_cansleep(rbni->gpio_read, 0); } static int rb91x_nand_dev_ready(struct mtd_info *mtd) { - void __iomem *base = ath79_gpio_base; + struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd); - return !!(__raw_readl(base + AR71XX_GPIO_REG_IN) & RB91X_NAND_RDY); + return gpio_get_value_cansleep(rbni->gpio_rdy); } static void rb91x_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { - if (ctrl & NAND_CTRL_CHANGE) { - u32 on = 0; - u32 off; - - if (!(ctrl & NAND_NCE)) - on |= RB91X_NAND_NCE; - - if (ctrl & NAND_CLE) - on |= RB91X_NAND_CLE; - - if (ctrl & NAND_ALE) - on |= RB91X_NAND_ALE; + struct rb91x_nand_info *rbni = mtd_to_rbinfo(mtd); - off = on ^ (RB91X_NAND_ALE | RB91X_NAND_NCE | RB91X_NAND_CLE); - rb91x_change_gpo(off, on); + 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(&t, 1); + 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(&data, 1); + rb91x_nand_read(rbni, &data, 1); return data; } static void rb91x_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) { - rb91x_nand_read(buf, 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) { - rb91x_nand_write(buf, 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 *info; + struct rb91x_nand_info *rbni; + struct rb91x_nand_platform_data *pdata; int ret; pr_info(DRV_DESC "\n"); - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + return -EINVAL; + + rbni = devm_kzalloc(&pdev->dev, sizeof(*rbni), GFP_KERNEL); + if (!rbni) return -ENOMEM; - info->chip.priv = &info; - info->mtd.priv = &info->chip; - info->mtd.owner = THIS_MODULE; + 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; - info->chip.cmd_ctrl = rb91x_nand_cmd_ctrl; - info->chip.dev_ready = rb91x_nand_dev_ready; - info->chip.read_byte = rb91x_nand_read_byte; - info->chip.write_buf = rb91x_nand_write_buf; - info->chip.read_buf = rb91x_nand_read_buf; + rbni->chip.priv = &rbni; + rbni->mtd.priv = &rbni->chip; + rbni->mtd.owner = THIS_MODULE; - info->chip.chip_delay = 25; - info->chip.ecc.mode = NAND_ECC_SOFT; + 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; - platform_set_drvdata(pdev, info); + rbni->chip.chip_delay = 25; + rbni->chip.ecc.mode = NAND_ECC_SOFT; - ret = nand_scan_ident(&info->mtd, 1, NULL); + platform_set_drvdata(pdev, rbni); + + ret = rb91x_nand_gpio_init(rbni); if (ret) return ret; - if (info->mtd.writesize == 512) - info->chip.ecc.layout = &rb91x_nand_ecclayout; + 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(&info->mtd); + ret = nand_scan_tail(&rbni->mtd); if (ret) return ret; - ret = mtd_device_register(&info->mtd, rb91x_nand_partitions, + ret = mtd_device_register(&rbni->mtd, rb91x_nand_partitions, ARRAY_SIZE(rb91x_nand_partitions)); if (ret) goto err_release_nand; @@ -325,7 +347,7 @@ static int rb91x_nand_probe(struct platform_device *pdev) return 0; err_release_nand: - nand_release(&info->mtd); + nand_release(&rbni->mtd); return ret; } @@ -342,7 +364,7 @@ static struct platform_driver rb91x_nand_driver = { .probe = rb91x_nand_probe, .remove = rb91x_nand_remove, .driver = { - .name = DRV_NAME, + .name = RB91X_NAND_DRIVER_NAME, .owner = THIS_MODULE, }, }; diff --git a/target/linux/ar71xx/files/include/linux/platform_data/rb91x_nand.h b/target/linux/ar71xx/files/include/linux/platform_data/rb91x_nand.h new file mode 100644 index 0000000000..5f17fb8148 --- /dev/null +++ b/target/linux/ar71xx/files/include/linux/platform_data/rb91x_nand.h @@ -0,0 +1,16 @@ +#ifndef _RB91X_NAND_H_ +#define _RB91X_NAND_H_ + +#define RB91X_NAND_DRIVER_NAME "rb91x-nand" + +struct rb91x_nand_platform_data { + int gpio_nce; /* chip enable, active low */ + int gpio_ale; /* address latch enable */ + int gpio_cle; /* command latch enable */ + int gpio_rdy; + int gpio_read; + int gpio_nrw; /* read/write enable, active low */ + int gpio_nle; /* latch enable, active low */ +}; + +#endif /* _RB91X_NAND_H_ */
\ No newline at end of file |