2 * drivers/mtd/devices/goldfish_nand.c
4 * Copyright (C) 2007 Google, Inc.
5 * Copyright (C) 2012 Intel, Inc.
6 * Copyright (C) 2013 Intel, Inc.
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.
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.
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>
29 #include <asm/div64.h>
31 #include "goldfish_nand_reg.h"
33 struct goldfish_nand
{
35 unsigned char __iomem
*base
;
36 struct cmd_params
*cmd_params
;
38 struct mtd_info mtd
[0];
41 static u32
goldfish_nand_cmd_with_params(struct mtd_info
*mtd
,
42 enum nand_cmd cmd
, u64 addr
, u32 len
,
46 struct goldfish_nand
*nand
= mtd
->priv
;
47 struct cmd_params
*cps
= nand
->cmd_params
;
48 unsigned char __iomem
*base
= nand
->base
;
55 cmdp
= NAND_CMD_ERASE_WITH_PARAMS
;
58 cmdp
= NAND_CMD_READ_WITH_PARAMS
;
61 cmdp
= NAND_CMD_WRITE_WITH_PARAMS
;
66 cps
->dev
= mtd
- nand
->mtd
;
67 cps
->addr_high
= (u32
)(addr
>> 32);
68 cps
->addr_low
= (u32
)addr
;
69 cps
->transfer_size
= len
;
71 writel(cmdp
, base
+ NAND_COMMAND
);
76 static u32
goldfish_nand_cmd(struct mtd_info
*mtd
, enum nand_cmd cmd
,
77 u64 addr
, u32 len
, void *ptr
)
79 struct goldfish_nand
*nand
= mtd
->priv
;
81 unsigned long irq_flags
;
82 unsigned char __iomem
*base
= nand
->base
;
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
);
94 spin_unlock_irqrestore(&nand
->lock
, irq_flags
);
98 static int goldfish_nand_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
100 loff_t ofs
= instr
->addr
;
101 u32 len
= instr
->len
;
104 if (ofs
+ len
> mtd
->size
)
106 rem
= do_div(ofs
, mtd
->writesize
);
109 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
111 if (len
% mtd
->writesize
)
113 len
= len
/ mtd
->writesize
* (mtd
->writesize
+ mtd
->oobsize
);
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
);
121 instr
->state
= MTD_ERASE_DONE
;
122 mtd_erase_callback(instr
);
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
);
132 static int goldfish_nand_read_oob(struct mtd_info
*mtd
, loff_t ofs
,
133 struct mtd_oob_ops
*ops
)
137 if (ofs
+ ops
->len
> mtd
->size
)
139 if (ops
->datbuf
&& ops
->len
&& ops
->len
!= mtd
->writesize
)
141 if (ops
->ooblen
+ ops
->ooboffs
> mtd
->oobsize
)
144 rem
= do_div(ofs
, mtd
->writesize
);
147 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
150 ops
->retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, ofs
,
151 ops
->len
, ops
->datbuf
);
152 ofs
+= mtd
->writesize
+ ops
->ooboffs
;
154 ops
->oobretlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, ofs
,
155 ops
->ooblen
, ops
->oobbuf
);
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
);
164 static int goldfish_nand_write_oob(struct mtd_info
*mtd
, loff_t ofs
,
165 struct mtd_oob_ops
*ops
)
169 if (ofs
+ ops
->len
> mtd
->size
)
171 if (ops
->len
&& ops
->len
!= mtd
->writesize
)
173 if (ops
->ooblen
+ ops
->ooboffs
> mtd
->oobsize
)
176 rem
= do_div(ofs
, mtd
->writesize
);
179 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
182 ops
->retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, ofs
,
183 ops
->len
, ops
->datbuf
);
184 ofs
+= mtd
->writesize
+ ops
->ooboffs
;
186 ops
->oobretlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, ofs
,
187 ops
->ooblen
, ops
->oobbuf
);
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
);
196 static int goldfish_nand_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
197 size_t *retlen
, u_char
*buf
)
201 if (from
+ len
> mtd
->size
)
203 if (len
!= mtd
->writesize
)
206 rem
= do_div(from
, mtd
->writesize
);
209 from
*= (mtd
->writesize
+ mtd
->oobsize
);
211 *retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, from
, len
, buf
);
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
);
220 static int goldfish_nand_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
221 size_t *retlen
, const u_char
*buf
)
225 if (to
+ len
> mtd
->size
)
227 if (len
!= mtd
->writesize
)
230 rem
= do_div(to
, mtd
->writesize
);
233 to
*= (mtd
->writesize
+ mtd
->oobsize
);
235 *retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, to
, len
, (void *)buf
);
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
);
244 static int goldfish_nand_block_isbad(struct mtd_info
*mtd
, loff_t ofs
)
248 if (ofs
>= mtd
->size
)
251 rem
= do_div(ofs
, mtd
->erasesize
);
254 ofs
*= mtd
->erasesize
/ mtd
->writesize
;
255 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
257 return goldfish_nand_cmd(mtd
, NAND_CMD_BLOCK_BAD_GET
, ofs
, 0, NULL
);
260 pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
261 ofs
, mtd
->size
, mtd
->writesize
);
265 static int goldfish_nand_block_markbad(struct mtd_info
*mtd
, loff_t ofs
)
269 if (ofs
>= mtd
->size
)
272 rem
= do_div(ofs
, mtd
->erasesize
);
275 ofs
*= mtd
->erasesize
/ mtd
->writesize
;
276 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
278 if (goldfish_nand_cmd(mtd
, NAND_CMD_BLOCK_BAD_SET
, ofs
, 0, NULL
) != 1)
283 pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
284 ofs
, mtd
->size
, mtd
->writesize
);
288 static int nand_setup_cmd_params(struct platform_device
*pdev
,
289 struct goldfish_nand
*nand
)
292 unsigned char __iomem
*base
= nand
->base
;
294 nand
->cmd_params
= devm_kzalloc(&pdev
->dev
,
295 sizeof(struct cmd_params
), GFP_KERNEL
);
296 if (!nand
->cmd_params
)
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
);
305 static int goldfish_nand_init_device(struct platform_device
*pdev
,
306 struct goldfish_nand
*nand
, int id
)
311 unsigned long irq_flags
;
312 unsigned char __iomem
*base
= nand
->base
;
313 struct mtd_info
*mtd
= &nand
->mtd
[id
];
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
;
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
);
337 mtd
->name
= name
= devm_kzalloc(&pdev
->dev
, name_len
+ 1, GFP_KERNEL
);
341 result
= goldfish_nand_cmd(mtd
, NAND_CMD_GET_DEV_NAME
, 0, name_len
,
343 if (result
!= name_len
) {
345 "goldfish_nand_init_device failed to get dev name %d != %d\n",
349 ((char *) mtd
->name
)[name_len
] = '\0';
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
);
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
;
368 if (mtd_device_register(mtd
, NULL
, 0))
374 static int goldfish_nand_probe(struct platform_device
*pdev
)
382 struct goldfish_nand
*nand
;
383 unsigned char __iomem
*base
;
385 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
389 base
= devm_ioremap(&pdev
->dev
, r
->start
, PAGE_SIZE
);
393 version
= readl(base
+ NAND_VERSION
);
394 if (version
!= NAND_VERSION_CURRENT
) {
396 "goldfish_nand_init: version mismatch, got %d, expected %d\n",
397 version
, NAND_VERSION_CURRENT
);
400 num_dev
= readl(base
+ NAND_NUM_DEV
);
404 nand
= devm_kzalloc(&pdev
->dev
, sizeof(*nand
) +
405 sizeof(struct mtd_info
) * num_dev
, GFP_KERNEL
);
409 spin_lock_init(&nand
->lock
);
411 nand
->mtd_count
= num_dev
;
412 platform_set_drvdata(pdev
, nand
);
415 for (i
= 0; i
< num_dev
; i
++) {
416 err
= goldfish_nand_init_device(pdev
, nand
, i
);
420 if (num_dev_working
== 0)
425 static int goldfish_nand_remove(struct platform_device
*pdev
)
427 struct goldfish_nand
*nand
= platform_get_drvdata(pdev
);
429 for (i
= 0; i
< nand
->mtd_count
; i
++) {
430 if (nand
->mtd
[i
].name
)
431 mtd_device_unregister(&nand
->mtd
[i
]);
436 static struct platform_driver goldfish_nand_driver
= {
437 .probe
= goldfish_nand_probe
,
438 .remove
= goldfish_nand_remove
,
440 .name
= "goldfish_nand"
444 module_platform_driver(goldfish_nand_driver
);
445 MODULE_LICENSE("GPL");