2b4c0a8288a97e6e8380b455e1c2e77aec42525b
[15.05/openwrt.git] / target / linux / mpc83xx / patches-3.18 / 203-mtd-add-rbppc_nand-driver.patch
1 --- a/drivers/mtd/nand/Kconfig
2 +++ b/drivers/mtd/nand/Kconfig
3 @@ -408,6 +408,13 @@ config MTD_NAND_PLATFORM
4           devices. You will need to provide platform-specific functions
5           via platform_data.
6  
7 +config MTD_NAND_RB_PPC
8 +       tristate "MikroTik RB333/600 NAND support"
9 +       depends on RB_PPC
10 +       help
11 +         This option enables support for the NAND device on MikroTik
12 +         RouterBOARD 333/600 series boards.
13 +
14  config MTD_NAND_ORION
15         tristate "NAND Flash support for Marvell Orion SoC"
16         depends on PLAT_ORION
17 --- a/drivers/mtd/nand/Makefile
18 +++ b/drivers/mtd/nand/Makefile
19 @@ -32,6 +32,7 @@ obj-$(CONFIG_MTD_NAND_CM_X270)                += cmx27
20  obj-$(CONFIG_MTD_NAND_PXA3xx)          += pxa3xx_nand.o
21  obj-$(CONFIG_MTD_NAND_TMIO)            += tmio_nand.o
22  obj-$(CONFIG_MTD_NAND_PLATFORM)                += plat_nand.o
23 +obj-$(CONFIG_MTD_NAND_RB_PPC)          += rbppc_nand.o
24  obj-$(CONFIG_MTD_NAND_PASEMI)          += pasemi_nand.o
25  obj-$(CONFIG_MTD_NAND_ORION)           += orion_nand.o
26  obj-$(CONFIG_MTD_NAND_FSL_ELBC)                += fsl_elbc_nand.o
27 --- /dev/null
28 +++ b/drivers/mtd/nand/rbppc_nand.c
29 @@ -0,0 +1,251 @@
30 +/*
31 + * Copyright (C) 2008-2009 Noah Fontes <nfontes@transtruct.org>
32 + * Copyright (C) 2009 Michael Guntsche <mike@it-loops.com>
33 + * Copyright (C) Mikrotik 2007
34 + *
35 + * This program is free software; you can redistribute it and/or modify it
36 + * under the terms of the GNU General Public License as published by the
37 + * Free Software Foundation; either version 2 of the License, or (at your
38 + * option) any later version.
39 + */
40 +
41 +#include <linux/init.h>
42 +#include <linux/module.h>
43 +#include <linux/mtd/nand.h>
44 +#include <linux/mtd/mtd.h>
45 +#include <linux/mtd/partitions.h>
46 +#include <linux/of_platform.h>
47 +#include <linux/of_device.h>
48 +#include <linux/slab.h>
49 +#include <linux/delay.h>
50 +#include <linux/of_address.h>
51 +#include <asm/io.h>
52 +
53 +#define DRV_NAME       "rbppc_nand"
54 +#define DRV_VERSION    "0.0.2"
55 +
56 +static struct mtd_info rmtd;
57 +static struct nand_chip rnand;
58 +
59 +struct rbppc_nand_info {
60 +       void *gpi;
61 +       void *gpo;
62 +       void *localbus;
63 +
64 +       unsigned gpio_rdy;
65 +       unsigned gpio_nce;
66 +       unsigned gpio_cle;
67 +       unsigned gpio_ale;
68 +       unsigned gpio_ctrls;
69 +};
70 +
71 +/* We must use the OOB layout from yaffs 1 if we want this to be recognized
72 + * properly. Borrowed from the OpenWRT patches for the RB532.
73 + *
74 + * See <https://dev.openwrt.org/browser/trunk/target/linux/rb532/
75 + * patches-2.6.28/025-rb532_nand_fixup.patch> for more details.
76 + */
77 +static struct nand_ecclayout rbppc_nand_oob_16 = {
78 +       .eccbytes = 6,
79 +       .eccpos = { 8, 9, 10, 13, 14, 15 },
80 +       .oobavail = 9,
81 +       .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
82 +};
83 +
84 +static struct mtd_partition rbppc_nand_partition_info[] = {
85 +       {
86 +               name: "kernel",
87 +               offset: 0,
88 +               size: 4 * 1024 * 1024,
89 +       },
90 +       {
91 +               name: "rootfs",
92 +               offset: MTDPART_OFS_NXTBLK,
93 +               size: MTDPART_SIZ_FULL,
94 +       },
95 +};
96 +
97 +static int rbppc_nand_dev_ready(struct mtd_info *mtd) {
98 +       struct nand_chip *chip = mtd->priv;
99 +       struct rbppc_nand_info *priv = chip->priv;
100 +
101 +       return in_be32(priv->gpi) & priv->gpio_rdy;
102 +}
103 +
104 +static void rbppc_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) {
105 +       struct nand_chip *chip = mtd->priv;
106 +       struct rbppc_nand_info *priv = chip->priv;
107 +
108 +       if (ctrl & NAND_CTRL_CHANGE) {
109 +               unsigned val = in_be32(priv->gpo);
110 +               if (!(val & priv->gpio_nce)) {
111 +                       /* make sure Local Bus has done NAND operations */
112 +                       readb(priv->localbus);
113 +               }
114 +
115 +               if (ctrl & NAND_CLE) {
116 +                       val |= priv->gpio_cle;
117 +               } else {
118 +                       val &= ~priv->gpio_cle;
119 +               }
120 +               if (ctrl & NAND_ALE) {
121 +                       val |= priv->gpio_ale;
122 +               } else {
123 +                       val &= ~priv->gpio_ale;
124 +               }
125 +               if (!(ctrl & NAND_NCE)) {
126 +                       val |= priv->gpio_nce;
127 +               } else {
128 +                       val &= ~priv->gpio_nce;
129 +               }
130 +               out_be32(priv->gpo, val);
131 +
132 +               /* make sure GPIO output has changed */
133 +               val ^= in_be32(priv->gpo);
134 +               if (val & priv->gpio_ctrls) {
135 +                       printk(KERN_ERR "rbppc_nand_hwcontrol: NAND GPO change failed 0x%08x\n", val);
136 +               }
137 +       }
138 +
139 +       if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W);
140 +}
141 +
142 +static void rbppc_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
143 +{
144 +       struct nand_chip *chip = mtd->priv;
145 +       memcpy(buf, chip->IO_ADDR_R, len);
146 +}
147 +
148 +static unsigned init_ok = 0;
149 +
150 +static int rbppc_nand_probe(struct platform_device *pdev)
151 +{
152 +       struct device_node *gpio;
153 +       struct device_node *nnand;
154 +       struct resource res;
155 +       struct rbppc_nand_info *info;
156 +       void *baddr;
157 +       const unsigned *rdy, *nce, *cle, *ale;
158 +
159 +       printk(KERN_INFO "rbppc_nand_probe: MikroTik RouterBOARD 333/600 series NAND driver, version " DRV_VERSION "\n");
160 +
161 +       info = kmalloc(sizeof(*info), GFP_KERNEL);
162 +
163 +       rdy = of_get_property(pdev->dev.of_node, "rdy", NULL);
164 +       nce = of_get_property(pdev->dev.of_node, "nce", NULL);
165 +       cle = of_get_property(pdev->dev.of_node, "cle", NULL);
166 +       ale = of_get_property(pdev->dev.of_node, "ale", NULL);
167 +
168 +       if (!rdy || !nce || !cle || !ale) {
169 +               printk(KERN_ERR "rbppc_nand_probe: GPIO properties are missing\n");
170 +               goto err;
171 +       }
172 +       if (rdy[0] != nce[0] || rdy[0] != cle[0] || rdy[0] != ale[0]) {
173 +               printk(KERN_ERR "rbppc_nand_probe: Different GPIOs are not supported\n");
174 +               goto err;
175 +       }
176 +
177 +       gpio = of_find_node_by_phandle(rdy[0]);
178 +       if (!gpio) {
179 +               printk(KERN_ERR "rbppc_nand_probe: No GPIO<%x> node found\n", *rdy);
180 +               goto err;
181 +       }
182 +       if (of_address_to_resource(gpio, 0, &res)) {
183 +               printk(KERN_ERR "rbppc_nand_probe: No reg property in GPIO found\n");
184 +               goto err;
185 +       }
186 +       info->gpo = ioremap_nocache(res.start, res.end - res.start + 1);
187 +
188 +       if (!of_address_to_resource(gpio, 1, &res)) {
189 +               info->gpi = ioremap_nocache(res.start, res.end - res.start + 1);
190 +       } else {
191 +               info->gpi = info->gpo;
192 +       }
193 +       of_node_put(gpio);
194 +
195 +       info->gpio_rdy = 1 << (31 - rdy[1]);
196 +       info->gpio_nce = 1 << (31 - nce[1]);
197 +       info->gpio_cle = 1 << (31 - cle[1]);
198 +       info->gpio_ale = 1 << (31 - ale[1]);
199 +       info->gpio_ctrls = info->gpio_nce | info->gpio_cle | info->gpio_ale;
200 +
201 +       nnand = of_find_node_by_name(NULL, "nnand");
202 +       if (!nnand) {
203 +               printk("rbppc_nand_probe: No nNAND found\n");
204 +               goto err;
205 +       }
206 +       if (of_address_to_resource(nnand, 0, &res)) {
207 +               printk("rbppc_nand_probe: No reg property in nNAND found\n");
208 +               goto err;
209 +       }
210 +       of_node_put(nnand);
211 +       info->localbus = ioremap_nocache(res.start, res.end - res.start + 1);
212 +
213 +       if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
214 +           printk("rbppc_nand_probe: No reg property found\n");
215 +           goto err;
216 +       }
217 +       baddr = ioremap_nocache(res.start, res.end - res.start + 1);
218 +
219 +       memset(&rnand, 0, sizeof(rnand));
220 +       rnand.cmd_ctrl = rbppc_nand_cmd_ctrl;
221 +       rnand.dev_ready = rbppc_nand_dev_ready;
222 +       rnand.read_buf = rbppc_nand_read_buf;
223 +       rnand.IO_ADDR_W = baddr;
224 +       rnand.IO_ADDR_R = baddr;
225 +       rnand.priv = info;
226 +
227 +       memset(&rmtd, 0, sizeof(rmtd));
228 +       rnand.ecc.mode = NAND_ECC_SOFT;
229 +       rnand.ecc.layout = &rbppc_nand_oob_16;
230 +       rnand.chip_delay = 25;
231 +       rmtd.priv = &rnand;
232 +       rmtd.owner = THIS_MODULE;
233 +
234 +       if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) {
235 +               printk(KERN_ERR "rbppc_nand_probe: RouterBOARD NAND device not found\n");
236 +               return -ENXIO;
237 +       }
238 +
239 +       mtd_device_parse_register(&rmtd, NULL, 0, rbppc_nand_partition_info, 2);
240 +       init_ok = 1;
241 +       return 0;
242 +
243 +err:
244 +       kfree(info);
245 +       return -1;
246 +}
247 +
248 +static struct of_device_id rbppc_nand_ids[] = {
249 +       { .name = "nand", },
250 +       { },
251 +};
252 +
253 +static struct platform_driver rbppc_nand_driver = {
254 +       .probe  = rbppc_nand_probe,
255 +       .driver = {
256 +               .name = "rbppc-nand",
257 +               .owner = THIS_MODULE,
258 +               .of_match_table = rbppc_nand_ids,
259 +       },
260 +};
261 +
262 +static int __init rbppc_nand_init(void)
263 +{
264 +       return platform_driver_register(&rbppc_nand_driver);
265 +}
266 +
267 +static void __exit rbppc_nand_exit(void)
268 +{
269 +       platform_driver_unregister(&rbppc_nand_driver);
270 +}
271 +
272 +MODULE_AUTHOR("Mikrotikls SIA");
273 +MODULE_AUTHOR("Noah Fontes");
274 +MODULE_AUTHOR("Michael Guntsche");
275 +MODULE_DESCRIPTION("MikroTik RouterBOARD 333/600 series NAND driver");
276 +MODULE_LICENSE("GPL");
277 +MODULE_VERSION(DRV_VERSION);
278 +
279 +module_init(rbppc_nand_init);
280 +module_exit(rbppc_nand_exit);