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
;
39 const uint32_t *wait_pattern
;
40 const uint32_t *wait_write
;
44 #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
46 static int fun_chip_ready(struct mtd_info
*mtd
)
48 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
50 if (gpio_get_value(fun
->rnb_gpio
))
53 dev_vdbg(fun
->dev
, "busy\n");
57 static void fun_wait_rnb(struct fsl_upm_nand
*fun
)
61 if (fun
->rnb_gpio
>= 0) {
62 while (--cnt
&& !fun_chip_ready(&fun
->mtd
))
67 dev_err(fun
->dev
, "tired waiting for RNB\n");
70 static void fun_cmd_ctrl(struct mtd_info
*mtd
, int cmd
, unsigned int ctrl
)
72 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
74 if (!(ctrl
& fun
->last_ctrl
)) {
75 fsl_upm_end_pattern(&fun
->upm
);
77 if (cmd
== NAND_CMD_NONE
)
80 fun
->last_ctrl
= ctrl
& (NAND_ALE
| NAND_CLE
);
83 if (ctrl
& NAND_CTRL_CHANGE
) {
85 fsl_upm_start_pattern(&fun
->upm
, fun
->upm_addr_offset
);
86 else if (ctrl
& NAND_CLE
)
87 fsl_upm_start_pattern(&fun
->upm
, fun
->upm_cmd_offset
);
90 fsl_upm_run_pattern(&fun
->upm
, fun
->io_base
, cmd
);
92 if (fun
->wait_pattern
)
96 static uint8_t fun_read_byte(struct mtd_info
*mtd
)
98 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
100 return in_8(fun
->chip
.IO_ADDR_R
);
103 static void fun_read_buf(struct mtd_info
*mtd
, uint8_t *buf
, int len
)
105 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
108 for (i
= 0; i
< len
; i
++)
109 buf
[i
] = in_8(fun
->chip
.IO_ADDR_R
);
112 static void fun_write_buf(struct mtd_info
*mtd
, const uint8_t *buf
, int len
)
114 struct fsl_upm_nand
*fun
= to_fsl_upm_nand(mtd
);
117 for (i
= 0; i
< len
; i
++) {
118 out_8(fun
->chip
.IO_ADDR_W
, buf
[i
]);
124 static int __devinit
fun_chip_init(struct fsl_upm_nand
*fun
)
127 #ifdef CONFIG_MTD_PARTITIONS
128 static const char *part_types
[] = { "cmdlinepart", NULL
, };
131 fun
->chip
.IO_ADDR_R
= fun
->io_base
;
132 fun
->chip
.IO_ADDR_W
= fun
->io_base
;
133 fun
->chip
.cmd_ctrl
= fun_cmd_ctrl
;
134 fun
->chip
.chip_delay
= fun
->chip_delay
;
135 fun
->chip
.read_byte
= fun_read_byte
;
136 fun
->chip
.read_buf
= fun_read_buf
;
137 fun
->chip
.write_buf
= fun_write_buf
;
138 fun
->chip
.ecc
.mode
= NAND_ECC_SOFT
;
140 if (fun
->rnb_gpio
>= 0)
141 fun
->chip
.dev_ready
= fun_chip_ready
;
143 fun
->mtd
.priv
= &fun
->chip
;
144 fun
->mtd
.owner
= THIS_MODULE
;
146 ret
= nand_scan(&fun
->mtd
, 1);
150 fun
->mtd
.name
= fun
->dev
->bus_id
;
152 #ifdef CONFIG_MTD_PARTITIONS
153 ret
= parse_mtd_partitions(&fun
->mtd
, part_types
, &fun
->parts
, 0);
155 return add_mtd_partitions(&fun
->mtd
, fun
->parts
, ret
);
157 return add_mtd_device(&fun
->mtd
);
160 static int __devinit
fun_probe(struct of_device
*ofdev
,
161 const struct of_device_id
*ofid
)
163 struct fsl_upm_nand
*fun
;
164 struct resource io_res
;
165 const uint32_t *prop
;
169 fun
= kzalloc(sizeof(*fun
), GFP_KERNEL
);
173 ret
= of_address_to_resource(ofdev
->node
, 0, &io_res
);
175 dev_err(&ofdev
->dev
, "can't get IO base\n");
179 ret
= fsl_upm_find(io_res
.start
, &fun
->upm
);
181 dev_err(&ofdev
->dev
, "can't find UPM\n");
185 prop
= of_get_property(ofdev
->node
, "fsl,upm-addr-offset", &size
);
186 if (!prop
|| size
!= sizeof(uint32_t)) {
187 dev_err(&ofdev
->dev
, "can't get UPM address offset\n");
191 fun
->upm_addr_offset
= *prop
;
193 prop
= of_get_property(ofdev
->node
, "fsl,upm-cmd-offset", &size
);
194 if (!prop
|| size
!= sizeof(uint32_t)) {
195 dev_err(&ofdev
->dev
, "can't get UPM command offset\n");
199 fun
->upm_cmd_offset
= *prop
;
201 fun
->rnb_gpio
= of_get_gpio(ofdev
->node
, 0);
202 if (fun
->rnb_gpio
>= 0) {
203 ret
= gpio_request(fun
->rnb_gpio
, ofdev
->dev
.bus_id
);
205 dev_err(&ofdev
->dev
, "can't request RNB gpio\n");
208 gpio_direction_input(fun
->rnb_gpio
);
209 } else if (fun
->rnb_gpio
== -EINVAL
) {
210 dev_err(&ofdev
->dev
, "specified RNB gpio is invalid\n");
214 fun
->io_base
= devm_ioremap_nocache(&ofdev
->dev
, io_res
.start
,
215 io_res
.end
- io_res
.start
+ 1);
221 fun
->dev
= &ofdev
->dev
;
222 fun
->last_ctrl
= NAND_CLE
;
223 fun
->wait_pattern
= of_get_property(ofdev
->node
, "fsl,wait-pattern",
225 fun
->wait_write
= of_get_property(ofdev
->node
, "fsl,wait-write", NULL
);
227 prop
= of_get_property(ofdev
->node
, "chip-delay", NULL
);
229 fun
->chip_delay
= *prop
;
231 fun
->chip_delay
= 50;
233 ret
= fun_chip_init(fun
);
237 dev_set_drvdata(&ofdev
->dev
, fun
);
241 if (fun
->rnb_gpio
>= 0)
242 gpio_free(fun
->rnb_gpio
);
249 static int __devexit
fun_remove(struct of_device
*ofdev
)
251 struct fsl_upm_nand
*fun
= dev_get_drvdata(&ofdev
->dev
);
253 nand_release(&fun
->mtd
);
255 if (fun
->rnb_gpio
>= 0)
256 gpio_free(fun
->rnb_gpio
);
263 static struct of_device_id of_fun_match
[] = {
264 { .compatible
= "fsl,upm-nand" },
267 MODULE_DEVICE_TABLE(of
, of_fun_match
);
269 static struct of_platform_driver of_fun_driver
= {
270 .name
= "fsl,upm-nand",
271 .match_table
= of_fun_match
,
273 .remove
= __devexit_p(fun_remove
),
276 static int __init
fun_module_init(void)
278 return of_register_platform_driver(&of_fun_driver
);
280 module_init(fun_module_init
);
282 static void __exit
fun_module_exit(void)
284 of_unregister_platform_driver(&of_fun_driver
);
286 module_exit(fun_module_exit
);
288 MODULE_LICENSE("GPL");
289 MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
290 MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
291 "LocalBus User-Programmable Machine");