cfi_mtd: Fix bug in last sector detection
[oweals/u-boot.git] / drivers / mtd / cfi_mtd.c
1 /*
2  * (C) Copyright 2008 Semihalf
3  *
4  * Written by: Piotr Ziecik <kosmo@semihalf.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
26 #include <common.h>
27 #include <flash.h>
28
29 #include <asm/errno.h>
30 #include <linux/mtd/mtd.h>
31
32 extern flash_info_t flash_info[];
33
34 static struct mtd_info cfi_mtd_info[CONFIG_SYS_MAX_FLASH_BANKS];
35 static char cfi_mtd_names[CONFIG_SYS_MAX_FLASH_BANKS][16];
36
37 static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
38 {
39         flash_info_t *fi = mtd->priv;
40         size_t a_start = fi->start[0] + instr->addr;
41         size_t a_end = a_start + instr->len;
42         int s_first = -1;
43         int s_last = -1;
44         int error, sect;
45
46         for (sect = 0; sect < fi->sector_count; sect++) {
47                 if (a_start == fi->start[sect])
48                         s_first = sect;
49
50                 if (sect < fi->sector_count - 1) {
51                         if (a_end == fi->start[sect + 1]) {
52                                 s_last = sect;
53                                 break;
54                         }
55                 } else {
56                         s_last = sect;
57                         break;
58                 }
59         }
60
61         if (s_first >= 0 && s_first <= s_last) {
62                 instr->state = MTD_ERASING;
63
64                 flash_set_verbose(0);
65                 error = flash_erase(fi, s_first, s_last);
66                 flash_set_verbose(1);
67
68                 if (error) {
69                         instr->state = MTD_ERASE_FAILED;
70                         return -EIO;
71                 }
72
73                 instr->state = MTD_ERASE_DONE;
74                 mtd_erase_callback(instr);
75                 return 0;
76         }
77
78         return -EINVAL;
79 }
80
81 static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
82         size_t *retlen, u_char *buf)
83 {
84         flash_info_t *fi = mtd->priv;
85         u_char *f = (u_char*)(fi->start[0]) + from;
86
87         memcpy(buf, f, len);
88         *retlen = len;
89
90         return 0;
91 }
92
93 static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
94         size_t *retlen, const u_char *buf)
95 {
96         flash_info_t *fi = mtd->priv;
97         u_long t = fi->start[0] + to;
98         int error;
99
100         flash_set_verbose(0);
101         error = write_buff(fi, (u_char*)buf, t, len);
102         flash_set_verbose(1);
103
104         if (!error) {
105                 *retlen = len;
106                 return 0;
107         }
108
109         return -EIO;
110 }
111
112 static void cfi_mtd_sync(struct mtd_info *mtd)
113 {
114         /*
115          * This function should wait until all pending operations
116          * finish. However this driver is fully synchronous, so
117          * this function returns immediately
118          */
119 }
120
121 static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
122 {
123         flash_info_t *fi = mtd->priv;
124
125         flash_set_verbose(0);
126         flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs,
127                                         fi->start[0] + ofs + len - 1, fi);
128         flash_set_verbose(1);
129
130         return 0;
131 }
132
133 static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
134 {
135         flash_info_t *fi = mtd->priv;
136
137         flash_set_verbose(0);
138         flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs,
139                                         fi->start[0] + ofs + len - 1, fi);
140         flash_set_verbose(1);
141
142         return 0;
143 }
144
145 static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
146 {
147         int sect_size = 0;
148         int sect;
149
150         /*
151          * Select the largest sector size as erasesize (e.g. for UBI)
152          */
153         for (sect = 0; sect < fi->sector_count; sect++) {
154                 if (flash_sector_size(fi, sect) > sect_size)
155                         sect_size = flash_sector_size(fi, sect);
156         }
157
158         mtd->erasesize = sect_size;
159
160         return 0;
161 }
162
163 int cfi_mtd_init(void)
164 {
165         struct mtd_info *mtd;
166         flash_info_t *fi;
167         int error, i;
168
169         for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
170                 fi = &flash_info[i];
171                 mtd = &cfi_mtd_info[i];
172
173                 memset(mtd, 0, sizeof(struct mtd_info));
174
175                 error = cfi_mtd_set_erasesize(mtd, fi);
176                 if (error)
177                         continue;
178
179                 sprintf(cfi_mtd_names[i], "nor%d", i);
180                 mtd->name               = cfi_mtd_names[i];
181                 mtd->type               = MTD_NORFLASH;
182                 mtd->flags              = MTD_CAP_NORFLASH;
183                 mtd->size               = fi->size;
184                 mtd->writesize          = 1;
185
186                 mtd->erase              = cfi_mtd_erase;
187                 mtd->read               = cfi_mtd_read;
188                 mtd->write              = cfi_mtd_write;
189                 mtd->sync               = cfi_mtd_sync;
190                 mtd->lock               = cfi_mtd_lock;
191                 mtd->unlock             = cfi_mtd_unlock;
192                 mtd->priv               = fi;
193
194                 if (add_mtd_device(mtd))
195                         return -ENOMEM;
196         }
197
198         return 0;
199 }