mtd: nand: pxa3xx: add support for Toshiba flash
[oweals/u-boot.git] / drivers / mtd / cfi_mtd.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008 Semihalf
4  *
5  * Written by: Piotr Ziecik <kosmo@semihalf.com>
6  */
7
8 #include <common.h>
9 #include <flash.h>
10 #include <malloc.h>
11
12 #include <linux/errno.h>
13 #include <linux/mtd/mtd.h>
14 #include <linux/mtd/concat.h>
15 #include <mtd/cfi_flash.h>
16
17 static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS];
18 static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16];
19 #ifdef CONFIG_MTD_CONCAT
20 static char c_mtd_name[16];
21 #endif
22
23 static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
24 {
25         flash_info_t *fi = mtd->priv;
26         size_t a_start = fi->start[0] + instr->addr;
27         size_t a_end = a_start + instr->len;
28         int s_first = -1;
29         int s_last = -1;
30         int error, sect;
31
32         for (sect = 0; sect < fi->sector_count; sect++) {
33                 if (a_start == fi->start[sect])
34                         s_first = sect;
35
36                 if (sect < fi->sector_count - 1) {
37                         if (a_end == fi->start[sect + 1]) {
38                                 s_last = sect;
39                                 break;
40                         }
41                 } else {
42                         s_last = sect;
43                         break;
44                 }
45         }
46
47         if (s_first >= 0 && s_first <= s_last) {
48                 instr->state = MTD_ERASING;
49
50                 flash_set_verbose(0);
51                 error = flash_erase(fi, s_first, s_last);
52                 flash_set_verbose(1);
53
54                 if (error) {
55                         instr->state = MTD_ERASE_FAILED;
56                         return -EIO;
57                 }
58
59                 instr->state = MTD_ERASE_DONE;
60                 mtd_erase_callback(instr);
61                 return 0;
62         }
63
64         return -EINVAL;
65 }
66
67 static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
68         size_t *retlen, u_char *buf)
69 {
70         flash_info_t *fi = mtd->priv;
71         u_char *f = (u_char*)(fi->start[0]) + from;
72
73         memcpy(buf, f, len);
74         *retlen = len;
75
76         return 0;
77 }
78
79 static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
80         size_t *retlen, const u_char *buf)
81 {
82         flash_info_t *fi = mtd->priv;
83         u_long t = fi->start[0] + to;
84         int error;
85
86         flash_set_verbose(0);
87         error = write_buff(fi, (u_char*)buf, t, len);
88         flash_set_verbose(1);
89
90         if (!error) {
91                 *retlen = len;
92                 return 0;
93         }
94
95         return -EIO;
96 }
97
98 static void cfi_mtd_sync(struct mtd_info *mtd)
99 {
100         /*
101          * This function should wait until all pending operations
102          * finish. However this driver is fully synchronous, so
103          * this function returns immediately
104          */
105 }
106
107 static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
108 {
109         flash_info_t *fi = mtd->priv;
110
111         flash_set_verbose(0);
112         flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs,
113                                         fi->start[0] + ofs + len - 1, fi);
114         flash_set_verbose(1);
115
116         return 0;
117 }
118
119 static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
120 {
121         flash_info_t *fi = mtd->priv;
122
123         flash_set_verbose(0);
124         flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs,
125                                         fi->start[0] + ofs + len - 1, fi);
126         flash_set_verbose(1);
127
128         return 0;
129 }
130
131 static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
132 {
133         int sect_size = 0;
134         int sect_size_old = 0;
135         int sect;
136         int regions = 0;
137         int numblocks = 0;
138         ulong offset;
139         ulong base_addr;
140
141         /*
142          * First detect the number of eraseregions so that we can allocate
143          * the array of eraseregions correctly
144          */
145         for (sect = 0; sect < fi->sector_count; sect++) {
146                 if (sect_size_old != flash_sector_size(fi, sect))
147                         regions++;
148                 sect_size_old = flash_sector_size(fi, sect);
149         }
150
151         switch (regions) {
152         case 0:
153                 return 1;
154         case 1: /* flash has uniform erase size */
155                 mtd->numeraseregions = 0;
156                 mtd->erasesize = sect_size_old;
157                 return 0;
158         }
159
160         mtd->numeraseregions = regions;
161         mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions);
162
163         /*
164          * Now detect the largest sector and fill the eraseregions
165          */
166         regions = 0;
167         base_addr = offset = fi->start[0];
168         sect_size_old = flash_sector_size(fi, 0);
169         for (sect = 0; sect < fi->sector_count; sect++) {
170                 if (sect_size_old != flash_sector_size(fi, sect)) {
171                         mtd->eraseregions[regions].offset = offset - base_addr;
172                         mtd->eraseregions[regions].erasesize = sect_size_old;
173                         mtd->eraseregions[regions].numblocks = numblocks;
174                         /* Now start counting the next eraseregions */
175                         numblocks = 0;
176                         regions++;
177                         offset = fi->start[sect];
178                 }
179                 numblocks++;
180
181                 /*
182                  * Select the largest sector size as erasesize (e.g. for UBI)
183                  */
184                 if (flash_sector_size(fi, sect) > sect_size)
185                         sect_size = flash_sector_size(fi, sect);
186
187                 sect_size_old = flash_sector_size(fi, sect);
188         }
189
190         /*
191          * Set the last region
192          */
193         mtd->eraseregions[regions].offset = offset - base_addr;
194         mtd->eraseregions[regions].erasesize = sect_size_old;
195         mtd->eraseregions[regions].numblocks = numblocks;
196
197         mtd->erasesize = sect_size;
198
199         return 0;
200 }
201
202 int cfi_mtd_init(void)
203 {
204         struct mtd_info *mtd;
205         flash_info_t *fi;
206         int error, i;
207 #ifdef CONFIG_MTD_CONCAT
208         int devices_found = 0;
209         struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS];
210 #endif
211
212         for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
213                 fi = &flash_info[i];
214                 mtd = &cfi_mtd_info[i];
215
216                 memset(mtd, 0, sizeof(struct mtd_info));
217
218                 error = cfi_mtd_set_erasesize(mtd, fi);
219                 if (error)
220                         continue;
221
222                 sprintf(cfi_mtd_names[i], "nor%d", i);
223                 mtd->name               = cfi_mtd_names[i];
224                 mtd->type               = MTD_NORFLASH;
225                 mtd->flags              = MTD_CAP_NORFLASH;
226                 mtd->size               = fi->size;
227                 mtd->writesize          = 1;
228                 mtd->writebufsize       = mtd->writesize;
229
230                 mtd->_erase             = cfi_mtd_erase;
231                 mtd->_read              = cfi_mtd_read;
232                 mtd->_write             = cfi_mtd_write;
233                 mtd->_sync              = cfi_mtd_sync;
234                 mtd->_lock              = cfi_mtd_lock;
235                 mtd->_unlock            = cfi_mtd_unlock;
236                 mtd->priv               = fi;
237
238                 if (add_mtd_device(mtd))
239                         return -ENOMEM;
240
241 #ifdef CONFIG_MTD_CONCAT
242                 mtd_list[devices_found++] = mtd;
243 #endif
244         }
245
246 #ifdef CONFIG_MTD_CONCAT
247         if (devices_found > 1) {
248                 /*
249                  * We detected multiple devices. Concatenate them together.
250                  */
251                 sprintf(c_mtd_name, "nor%d", devices_found);
252                 mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name);
253
254                 if (mtd == NULL)
255                         return -ENXIO;
256
257                 if (add_mtd_device(mtd))
258                         return -ENOMEM;
259         }
260 #endif /* CONFIG_MTD_CONCAT */
261
262         return 0;
263 }