Merge branch 'master' of git://git.denx.de/u-boot-arm
[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
36 static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
37 {
38         flash_info_t *fi = mtd->priv;
39         size_t a_start = fi->start[0] + instr->addr;
40         size_t a_end = a_start + instr->len;
41         int s_first = -1;
42         int s_last = -1;
43         int error, sect;
44
45         for (sect = 0; sect < fi->sector_count - 1; sect++) {
46                 if (a_start == fi->start[sect])
47                         s_first = sect;
48
49                 if (a_end == fi->start[sect + 1]) {
50                         s_last = sect;
51                         break;
52                 }
53         }
54
55         if (s_first >= 0 && s_first <= s_last) {
56                 instr->state = MTD_ERASING;
57
58                 flash_set_verbose(0);
59                 error = flash_erase(fi, s_first, s_last);
60                 flash_set_verbose(1);
61
62                 if (error) {
63                         instr->state = MTD_ERASE_FAILED;
64                         return -EIO;
65                 }
66
67                 instr->state = MTD_ERASE_DONE;
68                 mtd_erase_callback(instr);
69                 return 0;
70         }
71
72         return -EINVAL;
73 }
74
75 static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
76         size_t *retlen, u_char *buf)
77 {
78         flash_info_t *fi = mtd->priv;
79         u_char *f = (u_char*)(fi->start[0]) + from;
80
81         memcpy(buf, f, len);
82         *retlen = len;
83
84         return 0;
85 }
86
87 static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
88         size_t *retlen, const u_char *buf)
89 {
90         flash_info_t *fi = mtd->priv;
91         u_long t = fi->start[0] + to;
92         int error;
93
94         flash_set_verbose(0);
95         error = write_buff(fi, (u_char*)buf, t, len);
96         flash_set_verbose(1);
97
98         if (!error) {
99                 *retlen = len;
100                 return 0;
101         }
102
103         return -EIO;
104 }
105
106 static void cfi_mtd_sync(struct mtd_info *mtd)
107 {
108         /*
109          * This function should wait until all pending operations
110          * finish. However this driver is fully synchronous, so
111          * this function returns immediately
112          */
113 }
114
115 static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
116 {
117         flash_info_t *fi = mtd->priv;
118
119         flash_set_verbose(0);
120         flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs,
121                                         fi->start[0] + ofs + len - 1, fi);
122         flash_set_verbose(1);
123
124         return 0;
125 }
126
127 static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
128 {
129         flash_info_t *fi = mtd->priv;
130
131         flash_set_verbose(0);
132         flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs,
133                                         fi->start[0] + ofs + len - 1, fi);
134         flash_set_verbose(1);
135
136         return 0;
137 }
138
139 static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
140 {
141         int sect_size = 0;
142         int sect;
143
144         for (sect = 0; sect < fi->sector_count; sect++) {
145                 if (!sect_size) {
146                         sect_size = flash_sector_size(fi, sect);
147                         continue;
148                 }
149
150                 if (sect_size != flash_sector_size(fi, sect)) {
151                         sect_size = 0;
152                         break;
153                 }
154         }
155
156         if (!sect_size) {
157                 puts("cfi-mtd: devices with multiple sector sizes are"
158                                                         "not supported\n");
159                 return -EINVAL;
160         }
161
162         mtd->erasesize = sect_size;
163
164         return 0;
165 }
166
167 int cfi_mtd_init(void)
168 {
169         struct mtd_info *mtd;
170         flash_info_t *fi;
171         int error, i;
172
173         for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
174                 fi = &flash_info[i];
175                 mtd = &cfi_mtd_info[i];
176
177                 memset(mtd, 0, sizeof(struct mtd_info));
178
179                 error = cfi_mtd_set_erasesize(mtd, fi);
180                 if (error)
181                         continue;
182
183                 mtd->name               = CFI_MTD_DEV_NAME;
184                 mtd->type               = MTD_NORFLASH;
185                 mtd->flags              = MTD_CAP_NORFLASH;
186                 mtd->size               = fi->size;
187                 mtd->writesize          = 1;
188
189                 mtd->erase              = cfi_mtd_erase;
190                 mtd->read               = cfi_mtd_read;
191                 mtd->write              = cfi_mtd_write;
192                 mtd->sync               = cfi_mtd_sync;
193                 mtd->lock               = cfi_mtd_lock;
194                 mtd->unlock             = cfi_mtd_unlock;
195                 mtd->priv               = fi;
196
197                 if (add_mtd_device(mtd))
198                         return -ENOMEM;
199         }
200
201         return 0;
202 }