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/mtd/mtd.h>
26 #include <linux/platform_device.h>
27 #include <linux/mutex.h>
28 #include <linux/goldfish.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
;
70 cps
->data
= (unsigned long)ptr
;
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 char __iomem
*base
= nand
->base
;
83 mutex_lock(&nand
->lock
);
84 if (goldfish_nand_cmd_with_params(mtd
, cmd
, addr
, len
, ptr
, &rv
)) {
85 writel(mtd
- nand
->mtd
, base
+ NAND_DEV
);
86 writel((u32
)(addr
>> 32), base
+ NAND_ADDR_HIGH
);
87 writel((u32
)addr
, base
+ NAND_ADDR_LOW
);
88 writel(len
, base
+ NAND_TRANSFER_SIZE
);
89 gf_write64((u64
)ptr
, base
+ NAND_DATA
, base
+ NAND_DATA_HIGH
);
90 writel(cmd
, base
+ NAND_COMMAND
);
91 rv
= readl(base
+ NAND_RESULT
);
93 mutex_unlock(&nand
->lock
);
97 static int goldfish_nand_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
99 loff_t ofs
= instr
->addr
;
100 u32 len
= instr
->len
;
103 if (ofs
+ len
> mtd
->size
)
105 rem
= do_div(ofs
, mtd
->writesize
);
108 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
110 if (len
% mtd
->writesize
)
112 len
= len
/ mtd
->writesize
* (mtd
->writesize
+ mtd
->oobsize
);
114 if (goldfish_nand_cmd(mtd
, NAND_CMD_ERASE
, ofs
, len
, NULL
) != len
) {
115 pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n",
116 ofs
, len
, mtd
->size
, mtd
->erasesize
);
120 instr
->state
= MTD_ERASE_DONE
;
121 mtd_erase_callback(instr
);
126 pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n",
127 ofs
, len
, mtd
->size
, mtd
->erasesize
);
131 static int goldfish_nand_read_oob(struct mtd_info
*mtd
, loff_t ofs
,
132 struct mtd_oob_ops
*ops
)
136 if (ofs
+ ops
->len
> mtd
->size
)
138 if (ops
->datbuf
&& ops
->len
&& ops
->len
!= mtd
->writesize
)
140 if (ops
->ooblen
+ ops
->ooboffs
> mtd
->oobsize
)
143 rem
= do_div(ofs
, mtd
->writesize
);
146 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
149 ops
->retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, ofs
,
150 ops
->len
, ops
->datbuf
);
151 ofs
+= mtd
->writesize
+ ops
->ooboffs
;
153 ops
->oobretlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, ofs
,
154 ops
->ooblen
, ops
->oobbuf
);
158 pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
159 ofs
, ops
->len
, ops
->ooblen
, mtd
->size
, mtd
->writesize
);
163 static int goldfish_nand_write_oob(struct mtd_info
*mtd
, loff_t ofs
,
164 struct mtd_oob_ops
*ops
)
168 if (ofs
+ ops
->len
> mtd
->size
)
170 if (ops
->len
&& ops
->len
!= mtd
->writesize
)
172 if (ops
->ooblen
+ ops
->ooboffs
> mtd
->oobsize
)
175 rem
= do_div(ofs
, mtd
->writesize
);
178 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
181 ops
->retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, ofs
,
182 ops
->len
, ops
->datbuf
);
183 ofs
+= mtd
->writesize
+ ops
->ooboffs
;
185 ops
->oobretlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, ofs
,
186 ops
->ooblen
, ops
->oobbuf
);
190 pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
191 ofs
, ops
->len
, ops
->ooblen
, mtd
->size
, mtd
->writesize
);
195 static int goldfish_nand_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
196 size_t *retlen
, u_char
*buf
)
200 if (from
+ len
> mtd
->size
)
203 rem
= do_div(from
, mtd
->writesize
);
206 from
*= (mtd
->writesize
+ mtd
->oobsize
);
208 *retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_READ
, from
, len
, buf
);
212 pr_err("goldfish_nand_read: invalid read, start %llx, len %zx, dev_size %llx, write_size %x\n",
213 from
, len
, mtd
->size
, mtd
->writesize
);
217 static int goldfish_nand_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
218 size_t *retlen
, const u_char
*buf
)
222 if (to
+ len
> mtd
->size
)
225 rem
= do_div(to
, mtd
->writesize
);
228 to
*= (mtd
->writesize
+ mtd
->oobsize
);
230 *retlen
= goldfish_nand_cmd(mtd
, NAND_CMD_WRITE
, to
, len
, (void *)buf
);
234 pr_err("goldfish_nand_write: invalid write, start %llx, len %zx, dev_size %llx, write_size %x\n",
235 to
, len
, mtd
->size
, mtd
->writesize
);
239 static int goldfish_nand_block_isbad(struct mtd_info
*mtd
, loff_t ofs
)
243 if (ofs
>= mtd
->size
)
246 rem
= do_div(ofs
, mtd
->erasesize
);
249 ofs
*= mtd
->erasesize
/ mtd
->writesize
;
250 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
252 return goldfish_nand_cmd(mtd
, NAND_CMD_BLOCK_BAD_GET
, ofs
, 0, NULL
);
255 pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
256 ofs
, mtd
->size
, mtd
->writesize
);
260 static int goldfish_nand_block_markbad(struct mtd_info
*mtd
, loff_t ofs
)
264 if (ofs
>= mtd
->size
)
267 rem
= do_div(ofs
, mtd
->erasesize
);
270 ofs
*= mtd
->erasesize
/ mtd
->writesize
;
271 ofs
*= (mtd
->writesize
+ mtd
->oobsize
);
273 if (goldfish_nand_cmd(mtd
, NAND_CMD_BLOCK_BAD_SET
, ofs
, 0, NULL
) != 1)
278 pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
279 ofs
, mtd
->size
, mtd
->writesize
);
283 static int nand_setup_cmd_params(struct platform_device
*pdev
,
284 struct goldfish_nand
*nand
)
287 unsigned char __iomem
*base
= nand
->base
;
289 nand
->cmd_params
= devm_kzalloc(&pdev
->dev
,
290 sizeof(struct cmd_params
), GFP_KERNEL
);
291 if (!nand
->cmd_params
)
294 paddr
= __pa(nand
->cmd_params
);
295 writel((u32
)(paddr
>> 32), base
+ NAND_CMD_PARAMS_ADDR_HIGH
);
296 writel((u32
)paddr
, base
+ NAND_CMD_PARAMS_ADDR_LOW
);
300 static int goldfish_nand_init_device(struct platform_device
*pdev
,
301 struct goldfish_nand
*nand
, int id
)
306 unsigned char __iomem
*base
= nand
->base
;
307 struct mtd_info
*mtd
= &nand
->mtd
[id
];
310 mutex_lock(&nand
->lock
);
311 writel(id
, base
+ NAND_DEV
);
312 flags
= readl(base
+ NAND_DEV_FLAGS
);
313 name_len
= readl(base
+ NAND_DEV_NAME_LEN
);
314 mtd
->writesize
= readl(base
+ NAND_DEV_PAGE_SIZE
);
315 mtd
->size
= readl(base
+ NAND_DEV_SIZE_LOW
);
316 mtd
->size
|= (u64
)readl(base
+ NAND_DEV_SIZE_HIGH
) << 32;
317 mtd
->oobsize
= readl(base
+ NAND_DEV_EXTRA_SIZE
);
318 mtd
->oobavail
= mtd
->oobsize
;
319 mtd
->erasesize
= readl(base
+ NAND_DEV_ERASE_SIZE
) /
320 (mtd
->writesize
+ mtd
->oobsize
) * mtd
->writesize
;
321 do_div(mtd
->size
, mtd
->writesize
+ mtd
->oobsize
);
322 mtd
->size
*= mtd
->writesize
;
324 "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
325 id
, mtd
->size
, mtd
->writesize
,
326 mtd
->oobsize
, mtd
->erasesize
);
327 mutex_unlock(&nand
->lock
);
331 mtd
->name
= name
= devm_kzalloc(&pdev
->dev
, name_len
+ 1, GFP_KERNEL
);
335 result
= goldfish_nand_cmd(mtd
, NAND_CMD_GET_DEV_NAME
, 0, name_len
,
337 if (result
!= name_len
) {
339 "goldfish_nand_init_device failed to get dev name %d != %d\n",
343 ((char *) mtd
->name
)[name_len
] = '\0';
345 /* Setup the MTD structure */
346 mtd
->type
= MTD_NANDFLASH
;
347 mtd
->flags
= MTD_CAP_NANDFLASH
;
348 if (flags
& NAND_DEV_FLAG_READ_ONLY
)
349 mtd
->flags
&= ~MTD_WRITEABLE
;
350 if (flags
& NAND_DEV_FLAG_CMD_PARAMS_CAP
)
351 nand_setup_cmd_params(pdev
, nand
);
353 mtd
->owner
= THIS_MODULE
;
354 mtd
->_erase
= goldfish_nand_erase
;
355 mtd
->_read
= goldfish_nand_read
;
356 mtd
->_write
= goldfish_nand_write
;
357 mtd
->_read_oob
= goldfish_nand_read_oob
;
358 mtd
->_write_oob
= goldfish_nand_write_oob
;
359 mtd
->_block_isbad
= goldfish_nand_block_isbad
;
360 mtd
->_block_markbad
= goldfish_nand_block_markbad
;
362 if (mtd_device_register(mtd
, NULL
, 0))
368 static int goldfish_nand_probe(struct platform_device
*pdev
)
376 struct goldfish_nand
*nand
;
377 unsigned char __iomem
*base
;
379 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
383 base
= devm_ioremap(&pdev
->dev
, r
->start
, PAGE_SIZE
);
387 version
= readl(base
+ NAND_VERSION
);
388 if (version
!= NAND_VERSION_CURRENT
) {
390 "goldfish_nand_init: version mismatch, got %d, expected %d\n",
391 version
, NAND_VERSION_CURRENT
);
394 num_dev
= readl(base
+ NAND_NUM_DEV
);
398 nand
= devm_kzalloc(&pdev
->dev
, sizeof(*nand
) +
399 sizeof(struct mtd_info
) * num_dev
, GFP_KERNEL
);
403 mutex_init(&nand
->lock
);
405 nand
->mtd_count
= num_dev
;
406 platform_set_drvdata(pdev
, nand
);
409 for (i
= 0; i
< num_dev
; i
++) {
410 err
= goldfish_nand_init_device(pdev
, nand
, i
);
414 if (num_dev_working
== 0)
419 static int goldfish_nand_remove(struct platform_device
*pdev
)
421 struct goldfish_nand
*nand
= platform_get_drvdata(pdev
);
424 for (i
= 0; i
< nand
->mtd_count
; i
++) {
425 if (nand
->mtd
[i
].name
)
426 mtd_device_unregister(&nand
->mtd
[i
]);
431 static struct platform_driver goldfish_nand_driver
= {
432 .probe
= goldfish_nand_probe
,
433 .remove
= goldfish_nand_remove
,
435 .name
= "goldfish_nand"
439 module_platform_driver(goldfish_nand_driver
);
440 MODULE_LICENSE("GPL");