aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Crispin <blogic@openwrt.org>2012-08-23 14:28:32 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2012-09-29 10:05:18 -0400
commit99f2b107924c07bee0bae7151426495fb815ca6e (patch)
tree111e492991fc9a4a258ffff7548fb9786e04aa57
parentda3888cb84065a03d30b5f729b405e573bd0d66e (diff)
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: John Crispin <blogic@openwrt.org> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/nand/Kconfig8
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/xway_nand.c201
3 files changed, 210 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 7101e8a03259..ce5cf020cd76 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -580,4 +580,12 @@ config MTD_NAND_FSMC
580 Enables support for NAND Flash chips on the ST Microelectronics 580 Enables support for NAND Flash chips on the ST Microelectronics
581 Flexible Static Memory Controller (FSMC) 581 Flexible Static Memory Controller (FSMC)
582 582
583config MTD_NAND_XWAY
584 tristate "Support for NAND on Lantiq XWAY SoC"
585 depends on LANTIQ && SOC_TYPE_XWAY
586 select MTD_NAND_PLATFORM
587 help
588 Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
589 to the External Bus Unit (EBU).
590
583endif # MTD_NAND 591endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index ddee81811b4a..c4b0ab316bab 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -53,5 +53,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
53obj-$(CONFIG_MTD_NAND_RICOH) += r852.o 53obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
54obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o 54obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
55obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ 55obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
56obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
56 57
57nand-objs := nand_base.o nand_bbt.o 58nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
new file mode 100644
index 000000000000..3f81dc8f214c
--- /dev/null
+++ b/drivers/mtd/nand/xway_nand.c
@@ -0,0 +1,201 @@
1/*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU General Public License version 2 as published
4 * by the Free Software Foundation.
5 *
6 * Copyright © 2012 John Crispin <blogic@openwrt.org>
7 */
8
9#include <linux/mtd/nand.h>
10#include <linux/of_gpio.h>
11#include <linux/of_platform.h>
12
13#include <lantiq_soc.h>
14
15/* nand registers */
16#define EBU_ADDSEL1 0x24
17#define EBU_NAND_CON 0xB0
18#define EBU_NAND_WAIT 0xB4
19#define EBU_NAND_ECC0 0xB8
20#define EBU_NAND_ECC_AC 0xBC
21
22/* nand commands */
23#define NAND_CMD_ALE (1 << 2)
24#define NAND_CMD_CLE (1 << 3)
25#define NAND_CMD_CS (1 << 4)
26#define NAND_WRITE_CMD_RESET 0xff
27#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE)
28#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE)
29#define NAND_WRITE_DATA (NAND_CMD_CS)
30#define NAND_READ_DATA (NAND_CMD_CS)
31#define NAND_WAIT_WR_C (1 << 3)
32#define NAND_WAIT_RD (0x1)
33
34/* we need to tel the ebu which addr we mapped the nand to */
35#define ADDSEL1_MASK(x) (x << 4)
36#define ADDSEL1_REGEN 1
37
38/* we need to tell the EBU that we have nand attached and set it up properly */
39#define BUSCON1_SETUP (1 << 22)
40#define BUSCON1_BCGEN_RES (0x3 << 12)
41#define BUSCON1_WAITWRC2 (2 << 8)
42#define BUSCON1_WAITRDC2 (2 << 6)
43#define BUSCON1_HOLDC1 (1 << 4)
44#define BUSCON1_RECOVC1 (1 << 2)
45#define BUSCON1_CMULT4 1
46
47#define NAND_CON_CE (1 << 20)
48#define NAND_CON_OUT_CS1 (1 << 10)
49#define NAND_CON_IN_CS1 (1 << 8)
50#define NAND_CON_PRE_P (1 << 7)
51#define NAND_CON_WP_P (1 << 6)
52#define NAND_CON_SE_P (1 << 5)
53#define NAND_CON_CS_P (1 << 4)
54#define NAND_CON_CSMUX (1 << 1)
55#define NAND_CON_NANDM 1
56
57static void xway_reset_chip(struct nand_chip *chip)
58{
59 unsigned long nandaddr = (unsigned long) chip->IO_ADDR_W;
60 unsigned long flags;
61
62 nandaddr &= ~NAND_WRITE_ADDR;
63 nandaddr |= NAND_WRITE_CMD;
64
65 /* finish with a reset */
66 spin_lock_irqsave(&ebu_lock, flags);
67 writeb(NAND_WRITE_CMD_RESET, (void __iomem *) nandaddr);
68 while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
69 ;
70 spin_unlock_irqrestore(&ebu_lock, flags);
71}
72
73static void xway_select_chip(struct mtd_info *mtd, int chip)
74{
75
76 switch (chip) {
77 case -1:
78 ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON);
79 ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON);
80 break;
81 case 0:
82 ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON);
83 ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON);
84 break;
85 default:
86 BUG();
87 }
88}
89
90static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
91{
92 struct nand_chip *this = mtd->priv;
93 unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
94 unsigned long flags;
95
96 if (ctrl & NAND_CTRL_CHANGE) {
97 nandaddr &= ~(NAND_WRITE_CMD | NAND_WRITE_ADDR);
98 if (ctrl & NAND_CLE)
99 nandaddr |= NAND_WRITE_CMD;
100 else
101 nandaddr |= NAND_WRITE_ADDR;
102 this->IO_ADDR_W = (void __iomem *) nandaddr;
103 }
104
105 if (cmd != NAND_CMD_NONE) {
106 spin_lock_irqsave(&ebu_lock, flags);
107 writeb(cmd, this->IO_ADDR_W);
108 while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0)
109 ;
110 spin_unlock_irqrestore(&ebu_lock, flags);
111 }
112}
113
114static int xway_dev_ready(struct mtd_info *mtd)
115{
116 return ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD;
117}
118
119static unsigned char xway_read_byte(struct mtd_info *mtd)
120{
121 struct nand_chip *this = mtd->priv;
122 unsigned long nandaddr = (unsigned long) this->IO_ADDR_R;
123 unsigned long flags;
124 int ret;
125
126 spin_lock_irqsave(&ebu_lock, flags);
127 ret = ltq_r8((void __iomem *)(nandaddr + NAND_READ_DATA));
128 spin_unlock_irqrestore(&ebu_lock, flags);
129
130 return ret;
131}
132
133static int xway_nand_probe(struct platform_device *pdev)
134{
135 struct nand_chip *this = platform_get_drvdata(pdev);
136 unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
137 const __be32 *cs = of_get_property(pdev->dev.of_node,
138 "lantiq,cs", NULL);
139 u32 cs_flag = 0;
140
141 /* load our CS from the DT. Either we find a valid 1 or default to 0 */
142 if (cs && (*cs == 1))
143 cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1;
144
145 /* setup the EBU to run in NAND mode on our base addr */
146 ltq_ebu_w32(CPHYSADDR(nandaddr)
147 | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1);
148
149 ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2
150 | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1
151 | BUSCON1_CMULT4, LTQ_EBU_BUSCON1);
152
153 ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P
154 | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P
155 | cs_flag, EBU_NAND_CON);
156
157 /* finish with a reset */
158 xway_reset_chip(this);
159
160 return 0;
161}
162
163/* allow users to override the partition in DT using the cmdline */
164static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
165
166static struct platform_nand_data xway_nand_data = {
167 .chip = {
168 .nr_chips = 1,
169 .chip_delay = 30,
170 .part_probe_types = part_probes,
171 },
172 .ctrl = {
173 .probe = xway_nand_probe,
174 .cmd_ctrl = xway_cmd_ctrl,
175 .dev_ready = xway_dev_ready,
176 .select_chip = xway_select_chip,
177 .read_byte = xway_read_byte,
178 }
179};
180
181/*
182 * Try to find the node inside the DT. If it is available attach out
183 * platform_nand_data
184 */
185static int __init xway_register_nand(void)
186{
187 struct device_node *node;
188 struct platform_device *pdev;
189
190 node = of_find_compatible_node(NULL, NULL, "lantiq,nand-xway");
191 if (!node)
192 return -ENOENT;
193 pdev = of_find_device_by_node(node);
194 if (!pdev)
195 return -EINVAL;
196 pdev->dev.platform_data = &xway_nand_data;
197 of_node_put(node);
198 return 0;
199}
200
201subsys_initcall(xway_register_nand);