Merge branch 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / drivers / staging / goldfish / goldfish_nand.c
1 /*
2  * drivers/mtd/devices/goldfish_nand.c
3  *
4  * Copyright (C) 2007 Google, Inc.
5  * Copyright (C) 2012 Intel, Inc.
6  * Copyright (C) 2013 Intel, Inc.
7  *
8  * This software is licensed under the terms of the GNU General Public
9  * License version 2, as published by the Free Software Foundation, and
10  * may be copied, distributed, and modified under those terms.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  */
18
19 #include <linux/io.h>
20 #include <linux/device.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
23 #include <linux/ioport.h>
24 #include <linux/vmalloc.h>
25 #include <linux/init.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/platform_device.h>
28
29 #include <asm/div64.h>
30
31 #include "goldfish_nand_reg.h"
32
33 struct goldfish_nand {
34         spinlock_t              lock;
35         unsigned char __iomem  *base;
36         struct cmd_params       *cmd_params;
37         size_t                  mtd_count;
38         struct mtd_info         mtd[0];
39 };
40
41 static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd,
42                         enum nand_cmd cmd, u64 addr, u32 len,
43                         void *ptr, u32 *rv)
44 {
45         u32 cmdp;
46         struct goldfish_nand *nand = mtd->priv;
47         struct cmd_params *cps = nand->cmd_params;
48         unsigned char __iomem  *base = nand->base;
49
50         if (cps == NULL)
51                 return -1;
52
53         switch (cmd) {
54         case NAND_CMD_ERASE:
55                 cmdp = NAND_CMD_ERASE_WITH_PARAMS;
56                 break;
57         case NAND_CMD_READ:
58                 cmdp = NAND_CMD_READ_WITH_PARAMS;
59                 break;
60         case NAND_CMD_WRITE:
61                 cmdp = NAND_CMD_WRITE_WITH_PARAMS;
62                 break;
63         default:
64                 return -1;
65         }
66         cps->dev = mtd - nand->mtd;
67         cps->addr_high = (u32)(addr >> 32);
68         cps->addr_low = (u32)addr;
69         cps->transfer_size = len;
70         cps->data = (u32)ptr;
71         writel(cmdp, base + NAND_COMMAND);
72         *rv = cps->result;
73         return 0;
74 }
75
76 static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
77                                 u64 addr, u32 len, void *ptr)
78 {
79         struct goldfish_nand *nand = mtd->priv;
80         u32 rv;
81         unsigned long irq_flags;
82         unsigned char __iomem  *base = nand->base;
83
84         spin_lock_irqsave(&nand->lock, irq_flags);
85         if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) {
86                 writel(mtd - nand->mtd, base + NAND_DEV);
87                 writel((u32)(addr >> 32), base + NAND_ADDR_HIGH);
88                 writel((u32)addr, base + NAND_ADDR_LOW);
89                 writel(len, base + NAND_TRANSFER_SIZE);
90                 writel((u32)ptr, base + NAND_DATA);
91                 writel(cmd, base + NAND_COMMAND);
92                 rv = readl(base + NAND_RESULT);
93         }
94         spin_unlock_irqrestore(&nand->lock, irq_flags);
95         return rv;
96 }
97
98 static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
99 {
100         loff_t ofs = instr->addr;
101         u32 len = instr->len;
102         u32 rem;
103
104         if (ofs + len > mtd->size)
105                 goto invalid_arg;
106         rem = do_div(ofs, mtd->writesize);
107         if (rem)
108                 goto invalid_arg;
109         ofs *= (mtd->writesize + mtd->oobsize);
110
111         if (len % mtd->writesize)
112                 goto invalid_arg;
113         len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
114
115         if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
116                 pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n",
117                         ofs, len, mtd->size, mtd->erasesize);
118                 return -EIO;
119         }
120
121         instr->state = MTD_ERASE_DONE;
122         mtd_erase_callback(instr);
123
124         return 0;
125
126 invalid_arg:
127         pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n",
128                 ofs, len, mtd->size, mtd->erasesize);
129         return -EINVAL;
130 }
131
132 static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
133                                 struct mtd_oob_ops *ops)
134 {
135         u32 rem;
136
137         if (ofs + ops->len > mtd->size)
138                 goto invalid_arg;
139         if (ops->datbuf && ops->len && ops->len != mtd->writesize)
140                 goto invalid_arg;
141         if (ops->ooblen + ops->ooboffs > mtd->oobsize)
142                 goto invalid_arg;
143
144         rem = do_div(ofs, mtd->writesize);
145         if (rem)
146                 goto invalid_arg;
147         ofs *= (mtd->writesize + mtd->oobsize);
148
149         if (ops->datbuf)
150                 ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
151                                                 ops->len, ops->datbuf);
152         ofs += mtd->writesize + ops->ooboffs;
153         if (ops->oobbuf)
154                 ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
155                                                 ops->ooblen, ops->oobbuf);
156         return 0;
157
158 invalid_arg:
159         pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
160                 ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
161         return -EINVAL;
162 }
163
164 static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
165                                 struct mtd_oob_ops *ops)
166 {
167         u32 rem;
168
169         if (ofs + ops->len > mtd->size)
170                 goto invalid_arg;
171         if (ops->len && ops->len != mtd->writesize)
172                 goto invalid_arg;
173         if (ops->ooblen + ops->ooboffs > mtd->oobsize)
174                 goto invalid_arg;
175
176         rem = do_div(ofs, mtd->writesize);
177         if (rem)
178                 goto invalid_arg;
179         ofs *= (mtd->writesize + mtd->oobsize);
180
181         if (ops->datbuf)
182                 ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
183                                                 ops->len, ops->datbuf);
184         ofs += mtd->writesize + ops->ooboffs;
185         if (ops->oobbuf)
186                 ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
187                                                 ops->ooblen, ops->oobbuf);
188         return 0;
189
190 invalid_arg:
191         pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
192                 ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
193         return -EINVAL;
194 }
195
196 static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
197                                 size_t *retlen, u_char *buf)
198 {
199         u32 rem;
200
201         if (from + len > mtd->size)
202                 goto invalid_arg;
203         if (len != mtd->writesize)
204                 goto invalid_arg;
205
206         rem = do_div(from, mtd->writesize);
207         if (rem)
208                 goto invalid_arg;
209         from *= (mtd->writesize + mtd->oobsize);
210
211         *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
212         return 0;
213
214 invalid_arg:
215         pr_err("goldfish_nand_read: invalid read, start %llx, len %zx, dev_size %llx, write_size %x\n",
216                 from, len, mtd->size, mtd->writesize);
217         return -EINVAL;
218 }
219
220 static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
221                                 size_t *retlen, const u_char *buf)
222 {
223         u32 rem;
224
225         if (to + len > mtd->size)
226                 goto invalid_arg;
227         if (len != mtd->writesize)
228                 goto invalid_arg;
229
230         rem = do_div(to, mtd->writesize);
231         if (rem)
232                 goto invalid_arg;
233         to *= (mtd->writesize + mtd->oobsize);
234
235         *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
236         return 0;
237
238 invalid_arg:
239         pr_err("goldfish_nand_write: invalid write, start %llx, len %zx, dev_size %llx, write_size %x\n",
240                 to, len, mtd->size, mtd->writesize);
241         return -EINVAL;
242 }
243
244 static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
245 {
246         u32 rem;
247
248         if (ofs >= mtd->size)
249                 goto invalid_arg;
250
251         rem = do_div(ofs, mtd->erasesize);
252         if (rem)
253                 goto invalid_arg;
254         ofs *= mtd->erasesize / mtd->writesize;
255         ofs *= (mtd->writesize + mtd->oobsize);
256
257         return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
258
259 invalid_arg:
260         pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
261                 ofs, mtd->size, mtd->writesize);
262         return -EINVAL;
263 }
264
265 static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
266 {
267         u32 rem;
268
269         if (ofs >= mtd->size)
270                 goto invalid_arg;
271
272         rem = do_div(ofs, mtd->erasesize);
273         if (rem)
274                 goto invalid_arg;
275         ofs *= mtd->erasesize / mtd->writesize;
276         ofs *= (mtd->writesize + mtd->oobsize);
277
278         if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
279                 return -EIO;
280         return 0;
281
282 invalid_arg:
283         pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
284                 ofs, mtd->size, mtd->writesize);
285         return -EINVAL;
286 }
287
288 static int nand_setup_cmd_params(struct platform_device *pdev,
289                                                 struct goldfish_nand *nand)
290 {
291         u64 paddr;
292         unsigned char __iomem  *base = nand->base;
293
294         nand->cmd_params = devm_kzalloc(&pdev->dev,
295                                         sizeof(struct cmd_params), GFP_KERNEL);
296         if (!nand->cmd_params)
297                 return -1;
298
299         paddr = __pa(nand->cmd_params);
300         writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH);
301         writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW);
302         return 0;
303 }
304
305 static int goldfish_nand_init_device(struct platform_device *pdev,
306                                         struct goldfish_nand *nand, int id)
307 {
308         u32 name_len;
309         u32 result;
310         u32 flags;
311         unsigned long irq_flags;
312         unsigned char __iomem  *base = nand->base;
313         struct mtd_info *mtd = &nand->mtd[id];
314         char *name;
315
316         spin_lock_irqsave(&nand->lock, irq_flags);
317         writel(id, base + NAND_DEV);
318         flags = readl(base + NAND_DEV_FLAGS);
319         name_len = readl(base + NAND_DEV_NAME_LEN);
320         mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
321         mtd->size = readl(base + NAND_DEV_SIZE_LOW);
322         mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32;
323         mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
324         mtd->oobavail = mtd->oobsize;
325         mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
326                         (mtd->writesize + mtd->oobsize) * mtd->writesize;
327         do_div(mtd->size, mtd->writesize + mtd->oobsize);
328         mtd->size *= mtd->writesize;
329         dev_dbg(&pdev->dev,
330                 "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
331                        id, mtd->size, mtd->writesize,
332                        mtd->oobsize, mtd->erasesize);
333         spin_unlock_irqrestore(&nand->lock, irq_flags);
334
335         mtd->priv = nand;
336
337         mtd->name = name = devm_kzalloc(&pdev->dev, name_len + 1, GFP_KERNEL);
338         if (name == NULL)
339                 return -ENOMEM;
340
341         result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len,
342                                                                         name);
343         if (result != name_len) {
344                 dev_err(&pdev->dev,
345                         "goldfish_nand_init_device failed to get dev name %d != %d\n",
346                                result, name_len);
347                 return -ENODEV;
348         }
349         ((char *) mtd->name)[name_len] = '\0';
350
351         /* Setup the MTD structure */
352         mtd->type = MTD_NANDFLASH;
353         mtd->flags = MTD_CAP_NANDFLASH;
354         if (flags & NAND_DEV_FLAG_READ_ONLY)
355                 mtd->flags &= ~MTD_WRITEABLE;
356         if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP)
357                 nand_setup_cmd_params(pdev, nand);
358
359         mtd->owner = THIS_MODULE;
360         mtd->_erase = goldfish_nand_erase;
361         mtd->_read = goldfish_nand_read;
362         mtd->_write = goldfish_nand_write;
363         mtd->_read_oob = goldfish_nand_read_oob;
364         mtd->_write_oob = goldfish_nand_write_oob;
365         mtd->_block_isbad = goldfish_nand_block_isbad;
366         mtd->_block_markbad = goldfish_nand_block_markbad;
367
368         if (mtd_device_register(mtd, NULL, 0))
369                 return -EIO;
370
371         return 0;
372 }
373
374 static int goldfish_nand_probe(struct platform_device *pdev)
375 {
376         u32 num_dev;
377         int i;
378         int err;
379         u32 num_dev_working;
380         u32 version;
381         struct resource *r;
382         struct goldfish_nand *nand;
383         unsigned char __iomem  *base;
384
385         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
386         if (r == NULL)
387                 return -ENODEV;
388
389         base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
390         if (base == NULL)
391                 return -ENOMEM;
392
393         version = readl(base + NAND_VERSION);
394         if (version != NAND_VERSION_CURRENT) {
395                 dev_err(&pdev->dev,
396                         "goldfish_nand_init: version mismatch, got %d, expected %d\n",
397                                 version, NAND_VERSION_CURRENT);
398                 return -ENODEV;
399         }
400         num_dev = readl(base + NAND_NUM_DEV);
401         if (num_dev == 0)
402                 return -ENODEV;
403
404         nand = devm_kzalloc(&pdev->dev, sizeof(*nand) +
405                                 sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
406         if (nand == NULL)
407                 return -ENOMEM;
408
409         spin_lock_init(&nand->lock);
410         nand->base = base;
411         nand->mtd_count = num_dev;
412         platform_set_drvdata(pdev, nand);
413
414         num_dev_working = 0;
415         for (i = 0; i < num_dev; i++) {
416                 err = goldfish_nand_init_device(pdev, nand, i);
417                 if (err == 0)
418                         num_dev_working++;
419         }
420         if (num_dev_working == 0)
421                 return -ENODEV;
422         return 0;
423 }
424
425 static int goldfish_nand_remove(struct platform_device *pdev)
426 {
427         struct goldfish_nand *nand = platform_get_drvdata(pdev);
428         int i;
429         for (i = 0; i < nand->mtd_count; i++) {
430                 if (nand->mtd[i].name)
431                         mtd_device_unregister(&nand->mtd[i]);
432         }
433         return 0;
434 }
435
436 static struct platform_driver goldfish_nand_driver = {
437         .probe          = goldfish_nand_probe,
438         .remove         = goldfish_nand_remove,
439         .driver = {
440                 .name = "goldfish_nand"
441         }
442 };
443
444 module_platform_driver(goldfish_nand_driver);
445 MODULE_LICENSE("GPL");