sunxi: add support for 4.1
[openwrt.git] / target / linux / sunxi / patches-4.1 / 117-mtd-nand-add-hynix-init.patch
1 From 5c5e3963a1b58be1669da5da93f51dc339cd73d7 Mon Sep 17 00:00:00 2001
2 From: Boris BREZILLON <b.brezillon.dev@gmail.com>
3 Date: Mon, 24 Feb 2014 16:30:22 +0100
4 Subject: [PATCH] mtd: nand: Add hynix specific initializer
5
6 Add an hynix initiliazer to manage read retries on h27uxgt8t2a chip.
7
8 Signed-off-by: Boris BREZILLON <b.brezillon.dev@gmail.com>
9 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
10 ---
11  drivers/mtd/nand/Makefile     |   2 +-
12  drivers/mtd/nand/nand_hynix.c | 159 ++++++++++++++++++++++++++++++++++++++++++
13  drivers/mtd/nand/nand_ids.c   |   3 +-
14  include/linux/mtd/nand.h      |   2 +
15  4 files changed, 164 insertions(+), 2 deletions(-)
16  create mode 100644 drivers/mtd/nand/nand_hynix.c
17
18 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
19 index fcbe032..07b7c8c 100644
20 --- a/drivers/mtd/nand/Makefile
21 +++ b/drivers/mtd/nand/Makefile
22 @@ -5,7 +5,7 @@
23  obj-$(CONFIG_MTD_NAND)                 += nand.o
24  obj-$(CONFIG_MTD_NAND_ECC)             += nand_ecc.o
25  obj-$(CONFIG_MTD_NAND_BCH)             += nand_bch.o
26 -obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.o
27 +obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.o nand_hynix.o
28  obj-$(CONFIG_MTD_SM_COMMON)            += sm_common.o
29  
30  obj-$(CONFIG_MTD_NAND_CAFE)            += cafe_nand.o
31 diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c
32 new file mode 100644
33 index 0000000..0d051bf5
34 --- /dev/null
35 +++ b/drivers/mtd/nand/nand_hynix.c
36 @@ -0,0 +1,159 @@
37 +/*
38 + * Copyright (C) 2014 Boris BREZILLON <b.brezillon.dev@gmail.com>
39 + *
40 + * This program is free software; you can redistribute it and/or modify
41 + * it under the terms of the GNU General Public License as published by
42 + * the Free Software Foundation; either version 2 of the License, or
43 + * (at your option) any later version.
44 + *
45 + * This program is distributed in the hope that it will be useful,
46 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
48 + * GNU General Public License for more details.
49 + */
50 +
51 +#include <linux/module.h>
52 +#include <linux/mtd/nand.h>
53 +#include <linux/slab.h>
54 +
55 +static u8 h27ucg8t2a_read_retry_regs[] = {
56 +       0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf
57 +};
58 +
59 +struct hynix_read_retry {
60 +       u8 *regs;
61 +       u8 values[64];
62 +};
63 +
64 +struct hynix_nand {
65 +       struct hynix_read_retry read_retry;
66 +};
67 +
68 +int nand_setup_read_retry_hynix(struct mtd_info *mtd, int retry_mode)
69 +{
70 +       struct nand_chip *chip = mtd->priv;
71 +       struct hynix_nand *hynix = chip->manuf_priv;
72 +       int offset = retry_mode * 8;
73 +       int status;
74 +       int i;
75 +
76 +       chip->cmdfunc(mtd, 0x36, -1, -1);
77 +       for (i = 0; i < 8; i++) {
78 +               int column = hynix->read_retry.regs[i];
79 +               column |= column << 8;
80 +               chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
81 +               chip->write_byte(mtd, hynix->read_retry.values[offset + i]);
82 +       }
83 +       chip->cmdfunc(mtd, 0x16, -1, -1);
84 +
85 +       status = chip->waitfunc(mtd, chip);
86 +       if (status & NAND_STATUS_FAIL)
87 +               return -EIO;
88 +
89 +       return 0;
90 +}
91 +
92 +static void h27ucg8t2a_cleanup(struct mtd_info *mtd)
93 +{
94 +       struct nand_chip *chip = mtd->priv;
95 +       kfree(chip->manuf_priv);
96 +}
97 +
98 +static int h27ucg8t2a_init(struct mtd_info *mtd, const uint8_t *id)
99 +{
100 +       struct nand_chip *chip = mtd->priv;
101 +       struct hynix_nand *hynix;
102 +       u8 * buf = NULL;
103 +       int i, j;
104 +       int ret;
105 +
106 +       buf = kzalloc(1024, GFP_KERNEL);
107 +       if (!buf)
108 +               return -ENOMEM;
109 +
110 +       chip->select_chip(mtd, 0);
111 +       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
112 +       chip->cmdfunc(mtd, 0x36, 0xff, -1);
113 +       chip->write_byte(mtd, 0x40);
114 +       chip->cmdfunc(mtd, NAND_CMD_NONE, 0xcc, -1);
115 +       chip->write_byte(mtd, 0x4d);
116 +       chip->cmdfunc(mtd, 0x16, -1, -1);
117 +       chip->cmdfunc(mtd, 0x17, -1, -1);
118 +       chip->cmdfunc(mtd, 0x04, -1, -1);
119 +       chip->cmdfunc(mtd, 0x19, -1, -1);
120 +       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, 0x200);
121 +
122 +       chip->read_buf(mtd, buf, 2);
123 +       if (buf[0] != 0x8 || buf[1] != 0x8) {
124 +               ret = -EINVAL;
125 +               goto leave;
126 +       }
127 +       chip->read_buf(mtd, buf, 1024);
128 +
129 +       ret = 0;
130 +       for (j = 0; j < 8; j++) {
131 +               for (i = 0; i < 64; i++) {
132 +                       u8 *tmp = buf + (128 * j);
133 +                       if ((tmp[i] | tmp[i + 64]) != 0xff) {
134 +                               ret = -EINVAL;
135 +                               goto leave;
136 +                       }
137 +               }
138 +       }
139 +
140 +       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
141 +       chip->cmdfunc(mtd, 0x38, -1, -1);
142 +       chip->select_chip(mtd, -1);
143 +
144 +       if (!ret) {
145 +               hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
146 +               if (!hynix) {
147 +                       ret = -ENOMEM;
148 +                       goto leave;
149 +               }
150 +
151 +               hynix->read_retry.regs = h27ucg8t2a_read_retry_regs;
152 +               memcpy(hynix->read_retry.values, buf, 64);
153 +               chip->manuf_priv = hynix;
154 +               chip->setup_read_retry = nand_setup_read_retry_hynix;
155 +               chip->read_retries = 8;
156 +               chip->manuf_cleanup = h27ucg8t2a_cleanup;
157 +       }
158 +
159 +leave:
160 +       kfree(buf);
161 +
162 +       return ret;
163 +}
164 +
165 +struct hynix_nand_initializer {
166 +       u8 id[6];
167 +       int (*init)(struct mtd_info *mtd, const uint8_t *id);
168 +};
169 +
170 +struct hynix_nand_initializer initializers[] = {
171 +       {
172 +               .id = {NAND_MFR_HYNIX, 0xde, 0x94, 0xda, 0x74, 0xc4},
173 +               .init = h27ucg8t2a_init,
174 +       },
175 +};
176 +
177 +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id)
178 +{
179 +       int i;
180 +
181 +       for (i = 0; i < ARRAY_SIZE(initializers); i++) {
182 +               struct hynix_nand_initializer *initializer = &initializers[i];
183 +               if (memcmp(id, initializer->id, sizeof(initializer->id)))
184 +                       continue;
185 +
186 +               return initializer->init(mtd, id);
187 +       }
188 +
189 +       return 0;
190 +}
191 +EXPORT_SYMBOL(hynix_nand_init);
192 +
193 +MODULE_LICENSE("GPL");
194 +MODULE_AUTHOR("Boris BREZILLON <b.brezillon.dev@gmail.com>");
195 +MODULE_DESCRIPTION("Hynix NAND specific code");
196 diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
197 index dd620c1..b786718 100644
198 --- a/drivers/mtd/nand/nand_ids.c
199 +++ b/drivers/mtd/nand/nand_ids.c
200 @@ -163,6 +163,7 @@ struct nand_flash_dev nand_flash_ids[] = {
201         {NULL}
202  };
203  
204 +
205  /* Manufacturer IDs */
206  struct nand_manufacturers nand_manuf_ids[] = {
207         {NAND_MFR_TOSHIBA, "Toshiba"},
208 @@ -171,7 +172,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
209         {NAND_MFR_NATIONAL, "National"},
210         {NAND_MFR_RENESAS, "Renesas"},
211         {NAND_MFR_STMICRO, "ST Micro"},
212 -       {NAND_MFR_HYNIX, "Hynix"},
213 +       {NAND_MFR_HYNIX, "Hynix", hynix_nand_init},
214         {NAND_MFR_MICRON, "Micron"},
215         {NAND_MFR_AMD, "AMD/Spansion"},
216         {NAND_MFR_MACRONIX, "Macronix"},
217 diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
218 index 5844d6f..328aab2 100644
219 --- a/include/linux/mtd/nand.h
220 +++ b/include/linux/mtd/nand.h
221 @@ -959,6 +959,8 @@ struct nand_manufacturers {
222  extern struct nand_flash_dev nand_flash_ids[];
223  extern struct nand_manufacturers nand_manuf_ids[];
224  
225 +int hynix_nand_init(struct mtd_info *mtd, const uint8_t *id);
226 +
227  extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
228  extern int nand_default_bbt(struct mtd_info *mtd);
229  extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);