xref: /openbmc/u-boot/drivers/mtd/cfi_mtd.c (revision 3e3eec39)
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 }
203