]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/mtd/nand/kirkwood_nand.c
mtd: nand: kirkwood: enable BCH ECC and add support for dynamically switching ECC...
[karo-tx-uboot.git] / drivers / mtd / nand / kirkwood_nand.c
1 /*
2  * (C) Copyright 2009
3  * Marvell Semiconductor <www.marvell.com>
4  * Written-by: Prafulla Wadaskar <prafulla@marvell.com>
5  *
6  * See file CREDITS for list of people who contributed to this
7  * project.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  */
24
25 #include <common.h>
26 #include <asm/io.h>
27 #include <asm/arch/kirkwood.h>
28 #include <nand.h>
29 #include <linux/mtd/nand_ecc.h>
30 #include <linux/mtd/nand_bch.h>
31
32 #ifndef CONFIG_NAND_ECC_ALGO
33 #define CONFIG_NAND_ECC_ALGO    NAND_ECC_SOFT
34 #endif
35
36 /* NAND Flash Soc registers */
37 struct kwnandf_registers {
38         u32 rd_params;  /* 0x10418 */
39         u32 wr_param;   /* 0x1041c */
40         u8  pad[0x10470 - 0x1041c - 4];
41         u32 ctrl;       /* 0x10470 */
42 };
43
44 static struct kwnandf_registers *nf_reg =
45         (struct kwnandf_registers *)KW_NANDF_BASE;
46
47 /*
48  * hardware specific access to control-lines/bits
49  */
50 #define NAND_ACTCEBOOT_BIT              0x02
51
52 static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd,
53                               unsigned int ctrl)
54 {
55         struct nand_chip *nc = mtd->priv;
56         u32 offs;
57
58         if (cmd == NAND_CMD_NONE)
59                 return;
60
61         if (ctrl & NAND_CLE)
62                 offs = (1 << 0);        /* Commands with A[1:0] == 01 */
63         else if (ctrl & NAND_ALE)
64                 offs = (1 << 1);        /* Addresses with A[1:0] == 10 */
65         else
66                 return;
67
68         writeb(cmd, nc->IO_ADDR_W + offs);
69 }
70
71 void kw_nand_select_chip(struct mtd_info *mtd, int chip)
72 {
73         u32 data;
74
75         data = readl(&nf_reg->ctrl);
76         data |= NAND_ACTCEBOOT_BIT;
77         writel(data, &nf_reg->ctrl);
78 }
79
80 #ifdef CONFIG_NAND_ECC_SOFT_RS
81 static struct nand_ecclayout kw_nand_oob_rs = {
82         .eccbytes = 40,
83         .eccpos = {
84                 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
85                 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
86                 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
87                 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
88         },
89         .oobfree = {
90                 { .offset = 2, .length = 22, },
91         },
92 };
93
94 static void kw_nand_switch_eccmode(struct mtd_info *mtd,
95                                    nand_ecc_modes_t eccmode)
96 {
97         struct nand_chip *nand = mtd->priv;
98
99         if (nand->ecc.mode == eccmode)
100                 return;
101
102         switch (eccmode) {
103         case NAND_ECC_SOFT_RS:
104                 nand->ecc.mode = NAND_ECC_SOFT_RS;
105                 nand->ecc.layout = &kw_nand_oob_rs;
106                 nand->ecc.size = 512;
107                 nand->ecc.bytes = 10;
108                 break;
109         case NAND_ECC_SOFT_BCH:
110                 nand->ecc.layout = NULL;
111                 nand->ecc.bytes = 0;
112                 nand->ecc.size = 0;
113                 break;
114         case NAND_ECC_SOFT:
115                 nand->ecc.layout = NULL;
116                 nand->ecc.size = 0;
117                 break;
118         default:
119                 printf("Unsupported ecc mode %u\n", eccmode);
120                 return;
121         }
122         nand->ecc.mode = eccmode;
123         nand->options |= NAND_OWN_BUFFERS;
124         nand_scan_tail(mtd);
125         nand->options &= ~NAND_OWN_BUFFERS;
126 }
127
128 static int do_kw_nand_switch_eccmode(cmd_tbl_t *cmdtp, int flag, int argc,
129                                      char *const argv[])
130 {
131         nand_ecc_modes_t mode;
132         struct mtd_info *mtd;
133
134         if (argc > 2)
135                 return CMD_RET_USAGE;
136         if (nand_curr_device < 0 ||
137             nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
138             !nand_info[nand_curr_device].name) {
139                 printf("Error: Can't switch ecc, no devices available\n");
140                 return CMD_RET_FAILURE;
141         }
142         mtd = &nand_info[nand_curr_device];
143
144         if (argc < 2) {
145                 struct nand_chip *nand = mtd->priv;
146                 char *modestr;
147
148                 switch (nand->ecc.mode) {
149                 case NAND_ECC_SOFT:
150                         modestr = "HAMMING";
151                         break;
152                 case NAND_ECC_SOFT_RS:
153                         modestr = "RS";
154                         break;
155                 case NAND_ECC_SOFT_BCH:
156                         if (mtd_nand_has_bch()) {
157                                 modestr = "BCH";
158                                 break;
159                         }
160                         // fallthru
161                 default:
162                         printf("Unsupported ECC mode %d in use\n", nand->ecc.mode);
163                         return CMD_RET_FAILURE;
164                 }
165                 printf("NAND ECC mode: %s\n", modestr);
166                 return CMD_RET_SUCCESS;
167         }
168         if (strcmp(argv[1], "hamming") == 0 ||
169             strcmp(argv[1], "soft") == 0) {
170                 mode = NAND_ECC_SOFT;
171         } else if (strcmp(argv[1], "rs") == 0) {
172                 mode = NAND_ECC_SOFT_RS;
173         } else if (strcmp(argv[1], "bch") == 0) {
174                 mode = NAND_ECC_SOFT_BCH;
175         } else {
176                 printf("Unsupported ECC mode '%s'; supported modes are 'hamming', 'RS'%s\n",
177                        argv[1], mtd_nand_has_bch() ? ", 'BCH'" : "");
178                 return CMD_RET_USAGE;
179         }
180
181         kw_nand_switch_eccmode(mtd, mode);
182         printf("ECC mode switched to %s\n", argv[1]);
183         return CMD_RET_SUCCESS;
184 }
185 U_BOOT_CMD(nandecc, 2, 0, do_kw_nand_switch_eccmode,
186            "nandecc",
187            "switch ecc mode\n"
188            "\tvalid modes are:"
189            "HAMMING (1 bit ECC; not suitable for Micron NAND)"
190            "BCH 4 bit ECC; (default)"
191            "RS 4 bit ECC; (write only! for compatibility with ROM code)"
192            );
193 #endif
194
195 int board_nand_init(struct nand_chip *nand)
196 {
197         nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
198 #ifndef CONFIG_NAND_ECC_SOFT_RS
199         nand->ecc.mode = NAND_ECC_SOFT;
200 #else
201         nand->ecc.mode = NAND_ECC_SOFT_BCH;
202 #endif
203         nand->cmd_ctrl = kw_nand_hwcontrol;
204         nand->chip_delay = 40;
205         nand->select_chip = kw_nand_select_chip;
206         return 0;
207 }