Commit 99f2b107 authored by John Crispin's avatar John Crispin Committed by David Woodhouse

mtd: lantiq: Add NAND support on Lantiq XWAY SoC.

The driver uses plat_nand. As the platform_device is loaded from DT, we need
to lookup the node and attach our xway specific "struct platform_nand_data"
to it.
Signed-off-by: default avatarJohn Crispin <blogic@openwrt.org>
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent da3888cb
...@@ -580,4 +580,12 @@ config MTD_NAND_FSMC ...@@ -580,4 +580,12 @@ config MTD_NAND_FSMC
Enables support for NAND Flash chips on the ST Microelectronics Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC) Flexible Static Memory Controller (FSMC)
config MTD_NAND_XWAY
tristate "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
select MTD_NAND_PLATFORM
help
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).
endif # MTD_NAND endif # MTD_NAND
...@@ -53,5 +53,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o ...@@ -53,5 +53,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
nand-objs := nand_base.o nand_bbt.o nand-objs := nand_base.o nand_bbt.o
/*
* 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.
*
* Copyright © 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/mtd/nand.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <lantiq_soc.h>
/* nand registers */
#define EBU_ADDSEL1 0x24
#define EBU_NAND_CON 0xB0
#define EBU_NAND_WAIT 0xB4
#define EBU_NAND_ECC0 0xB8
#define EBU_NAND_ECC_AC 0xBC
/* nand commands */
#define NAND_CMD_ALE (1 << 2)
#define NAND_CMD_CLE (1 << 3)
#define NAND_CMD_CS (1 << 4)
#define NAND_WRITE_CMD_RESET 0xff
#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE)
#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE)
#define NAND_WRITE_DATA (NAND_CMD_CS)
#define NAND_READ_DATA (NAND_CMD_CS)
#define NAND_WAIT_WR_C (1 << 3)
#define NAND_WAIT_RD (0x1)
/* we need to tel the ebu which addr we mapped the nand to */
#define ADDSEL1_MASK(x) (x << 4)
#define ADDSEL1_REGEN 1
/* we need to tell the EBU that we have nand attached and set it up properly */
#define BUSCON1_SETUP (1 << 22)
#define BUSCON1_BCGEN_RES (0x3 << 12)
#define BUSCON1_WAITWRC2 (2 << 8)
#define BUSCON1_WAITRDC2 (2 << 6)
#define BUSCON1_HOLDC1 (1 << 4)
#define BUSCON1_RECOVC1 (1 << 2)
#define BUSCON1_CMULT4 1
#define NAND_CON_CE (1 << 20)
#define NAND_CON_OUT_CS1 (1 << 10)
#define NAND_CON_IN_CS1 (1 << 8)
#define NAND_CON_PRE_P (1 << 7)
#define NAND_CON_WP_P (1 << 6)
#define NAND_CON_SE_P (1 << 5)
#define NAND_CON_CS_P (1 << 4)
#define NAND_CON_CSMUX (1 << 1)
#define NAND_CON_NANDM 1
static void xway_reset_chip(struct nand_chip *chip)
{
unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W;
unsigned long flags;
nandaddr &= ~NAND_WRITE_ADDR;
nandaddr |= NAND_WRITE_CMD;
/* finish with a reset */
spin_lock_irqsave(&ebu_lock, flags);
writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
}
static void xway_select_chip(struct mtd_info *mtd, int chip)
{
switch (chip) {
case -1:
ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON);
ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON);
break;
case 0:
ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON);
ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON);
break;
default:
BUG();
}
}
static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
unsigned long flags;
if (ctrl & NAND_CTRL_CHANGE) {
nandaddr &= ~(NAND_WRITE_CMD | NAND_WRITE_ADDR);
if (ctrl & NAND_CLE)
nandaddr |= NAND_WRITE_CMD;
else
nandaddr |= NAND_WRITE_ADDR;
this->IO_ADDR_W = (void __iomem *) nandaddr;
}
if (cmd != NAND_CMD_NONE) {
spin_lock_irqsave(&ebu_lock, flags);
writeb(cmd, this->IO_ADDR_W);
while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
;
spin_unlock_irqrestore(&ebu_lock, flags);
}
}
static int xway_dev_ready(struct mtd_info *mtd)
{
return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD;
}
static unsigned char xway_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
unsigned long nandaddr = (unsigned long) this->IO_ADDR_R;
unsigned long flags;
int ret;
spin_lock_irqsave(&ebu_lock, flags);
ret = ltq_r8((void __iomem *)(nandaddr + NAND_READ_DATA));
spin_unlock_irqrestore(&ebu_lock, flags);
return ret;
}
static int xway_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this = platform_get_drvdata(pdev);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
const __be32 *cs = of_get_property(pdev->dev.of_node,
"lantiq,cs", NULL);
u32 cs_flag = 0;
/* load our CS from the DT. Either we find a valid 1 or default to 0 */
if (cs && (*cs == 1))
cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1;
/* setup the EBU to run in NAND mode on our base addr */
ltq_ebu_w32(CPHYSADDR(nandaddr)
| ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1);
ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2
| BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
| BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P
| NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
| cs_flag, EBU_NAND_CON);
/* finish with a reset */
xway_reset_chip(this);
return 0;
}
/* allow users to override the partition in DT using the cmdline */
static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
static struct platform_nand_data xway_nand_data = {
.chip = {
.nr_chips = 1,
.chip_delay = 30,
.part_probe_types = part_probes,
},
.ctrl = {
.probe = xway_nand_probe,
.cmd_ctrl = xway_cmd_ctrl,
.dev_ready = xway_dev_ready,
.select_chip = xway_select_chip,
.read_byte = xway_read_byte,
}
};
/*
* Try to find the node inside the DT. If it is available attach out
* platform_nand_data
*/
static int __init xway_register_nand(void)
{
struct device_node *node;
struct platform_device *pdev;
node = of_find_compatible_node(NULL, NULL, "lantiq,nand-xway");
if (!node)
return -ENOENT;
pdev = of_find_device_by_node(node);
if (!pdev)
return -EINVAL;
pdev->dev.platform_data = &xway_nand_data;
of_node_put(node);
return 0;
}
subsys_initcall(xway_register_nand);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment