2 * Freescale UPM NAND driver.
4 * Copyright © 2007-2008 MontaVista Software, Inc.
6 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/mtd/nand.h>
17 #include <linux/mtd/nand_ecc.h>
18 #include <linux/mtd/partitions.h>
19 #include <linux/mtd/mtd.h>
20 #include <linux/of_platform.h>
21 #include <linux/of_gpio.h>
23 #include <asm/fsl_lbc.h>
28 struct nand_chip chip
;
30 #ifdef CONFIG_MTD_PARTITIONS
31 struct mtd_partition
*parts
;
35 uint8_t upm_addr_offset
;
36 uint8_t upm_cmd_offset
;
37 void __iomem
*io_base
;
41 #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
43 static int fun_chip_ready(struct mtd_info
*mtd
)
45 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
47 if (gpio_get_value(fun
->rnb_gpio
))
50 dev_vdbg(fun
->dev
, "busy\n");
54 static void fun_wait_rnb(struct fsl_upm_nand
*fun
)
58 if (fun
->rnb_gpio
>= 0) {
59 while (--cnt
&& !fun_chip_ready(&fun
->mtd
))
64 dev_err(fun
->dev
, "tired waiting for RNB\n");
67 static void fun_cmd_ctrl(struct mtd_info
*mtd
, int cmd
, unsigned int ctrl
)
69 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
71 if (!(ctrl
& fun
->last_ctrl
)) {
72 fsl_upm_end_pattern(&fun
->upm
);
74 if (cmd
== NAND_CMD_NONE
)
77 fun
->last_ctrl
= ctrl
& (NAND_ALE
| NAND_CLE
);
80 if (ctrl
& NAND_CTRL_CHANGE
) {
82 fsl_upm_start_pattern(&fun
->upm
, fun
->upm_addr_offset
);
83 else if (ctrl
& NAND_CLE
)
84 fsl_upm_start_pattern(&fun
->upm
, fun
->upm_cmd_offset
);
87 fsl_upm_run_pattern(&fun
->upm
, fun
->io_base
, cmd
);
92 static uint8_t fun_read_byte(struct mtd_info
*mtd
)
94 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
96 return in_8(fun
->chip
.IO_ADDR_R
);
99 static void fun_read_buf(struct mtd_info
*mtd
, uint8_t *buf
, int len
)
101 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
104 for (i
= 0; i
< len
; i
++)
105 buf
[i
] = in_8(fun
->chip
.IO_ADDR_R
);
108 static void fun_write_buf(struct mtd_info
*mtd
, const uint8_t *buf
, int len
)
110 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
113 for (i
= 0; i
< len
; i
++) {
114 out_8(fun
->chip
.IO_ADDR_W
, buf
[i
]);
119 static int __devinit
fun_chip_init(struct fsl_upm_nand
*fun
,
120 const struct device_node
*upm_np
,
121 const struct resource
*io_res
)
124 struct device_node
*flash_np
;
125 #ifdef CONFIG_MTD_PARTITIONS
126 static const char *part_types
[] = { "cmdlinepart", NULL
, };
129 fun
->chip
.IO_ADDR_R
= fun
->io_base
;
130 fun
->chip
.IO_ADDR_W
= fun
->io_base
;
131 fun
->chip
.cmd_ctrl
= fun_cmd_ctrl
;
132 fun
->chip
.chip_delay
= 50;
133 fun
->chip
.read_byte
= fun_read_byte
;
134 fun
->chip
.read_buf
= fun_read_buf
;
135 fun
->chip
.write_buf
= fun_write_buf
;
136 fun
->chip
.ecc
.mode
= NAND_ECC_SOFT
;
138 if (fun
->rnb_gpio
>= 0)
139 fun
->chip
.dev_ready
= fun_chip_ready
;
141 fun
->mtd
.priv
= &fun
->chip
;
142 fun
->mtd
.owner
= THIS_MODULE
;
144 flash_np
= of_get_next_child(upm_np
, NULL
);
148 fun
->mtd
.name
= kasprintf(GFP_KERNEL
, "%x.%s", io_res
->start
,
150 if (!fun
->mtd
.name
) {
155 ret
= nand_scan(&fun
->mtd
, 1);
159 #ifdef CONFIG_MTD_PARTITIONS
160 ret
= parse_mtd_partitions(&fun
->mtd
, part_types
, &fun
->parts
, 0);
162 #ifdef CONFIG_MTD_OF_PARTS
164 ret
= of_mtd_parse_partitions(fun
->dev
, &fun
->mtd
,
165 flash_np
, &fun
->parts
);
168 ret
= add_mtd_partitions(&fun
->mtd
, fun
->parts
, ret
);
171 ret
= add_mtd_device(&fun
->mtd
);
173 of_node_put(flash_np
);
177 static int __devinit
fun_probe(struct of_device
*ofdev
,
178 const struct of_device_id
*ofid
)
180 struct fsl_upm_nand
*fun
;
181 struct resource io_res
;
182 const uint32_t *prop
;
186 fun
= kzalloc(sizeof(*fun
), GFP_KERNEL
);
190 ret
= of_address_to_resource(ofdev
->node
, 0, &io_res
);
192 dev_err(&ofdev
->dev
, "can't get IO base\n");
196 ret
= fsl_upm_find(io_res
.start
, &fun
->upm
);
198 dev_err(&ofdev
->dev
, "can't find UPM\n");
202 prop
= of_get_property(ofdev
->node
, "fsl,upm-addr-offset", &size
);
203 if (!prop
|| size
!= sizeof(uint32_t)) {
204 dev_err(&ofdev
->dev
, "can't get UPM address offset\n");
208 fun
->upm_addr_offset
= *prop
;
210 prop
= of_get_property(ofdev
->node
, "fsl,upm-cmd-offset", &size
);
211 if (!prop
|| size
!= sizeof(uint32_t)) {
212 dev_err(&ofdev
->dev
, "can't get UPM command offset\n");
216 fun
->upm_cmd_offset
= *prop
;
218 fun
->rnb_gpio
= of_get_gpio(ofdev
->node
, 0);
219 if (fun
->rnb_gpio
>= 0) {
220 ret
= gpio_request(fun
->rnb_gpio
, ofdev
->dev
.bus_id
);
222 dev_err(&ofdev
->dev
, "can't request RNB gpio\n");
225 gpio_direction_input(fun
->rnb_gpio
);
226 } else if (fun
->rnb_gpio
== -EINVAL
) {
227 dev_err(&ofdev
->dev
, "specified RNB gpio is invalid\n");
231 fun
->io_base
= devm_ioremap_nocache(&ofdev
->dev
, io_res
.start
,
232 io_res
.end
- io_res
.start
+ 1);
238 fun
->dev
= &ofdev
->dev
;
239 fun
->last_ctrl
= NAND_CLE
;
241 ret
= fun_chip_init(fun
, ofdev
->node
, &io_res
);
245 dev_set_drvdata(&ofdev
->dev
, fun
);
249 if (fun
->rnb_gpio
>= 0)
250 gpio_free(fun
->rnb_gpio
);
257 static int __devexit
fun_remove(struct of_device
*ofdev
)
259 struct fsl_upm_nand
*fun
= dev_get_drvdata(&ofdev
->dev
);
261 nand_release(&fun
->mtd
);
262 kfree(fun
->mtd
.name
);
264 if (fun
->rnb_gpio
>= 0)
265 gpio_free(fun
->rnb_gpio
);
272 static struct of_device_id of_fun_match
[] = {
273 { .compatible
= "fsl,upm-nand" },
276 MODULE_DEVICE_TABLE(of
, of_fun_match
);
278 static struct of_platform_driver of_fun_driver
= {
279 .name
= "fsl,upm-nand",
280 .match_table
= of_fun_match
,
282 .remove
= __devexit_p(fun_remove
),
285 static int __init
fun_module_init(void)
287 return of_register_platform_driver(&of_fun_driver
);
289 module_init(fun_module_init
);
291 static void __exit
fun_module_exit(void)
293 of_unregister_platform_driver(&of_fun_driver
);
295 module_exit(fun_module_exit
);
297 MODULE_LICENSE("GPL");
298 MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
299 MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
300 "LocalBus User-Programmable Machine");