2 * (C) Copyright 2008 Semihalf
4 * Written by: Piotr Ziecik <kosmo@semihalf.com>
6 * See file CREDITS for list of people who contributed to this
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.
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.
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,
30 #include <asm/errno.h>
31 #include <linux/mtd/mtd.h>
32 #include <linux/mtd/concat.h>
34 extern flash_info_t flash_info
[];
36 static struct mtd_info cfi_mtd_info
[CONFIG_SYS_MAX_FLASH_BANKS
];
37 static char cfi_mtd_names
[CONFIG_SYS_MAX_FLASH_BANKS
][16];
38 #ifdef CONFIG_MTD_CONCAT
39 static char c_mtd_name
[16];
42 static int cfi_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
44 flash_info_t
*fi
= mtd
->priv
;
45 size_t a_start
= fi
->start
[0] + instr
->addr
;
46 size_t a_end
= a_start
+ instr
->len
;
51 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
52 if (a_start
== fi
->start
[sect
])
55 if (sect
< fi
->sector_count
- 1) {
56 if (a_end
== fi
->start
[sect
+ 1]) {
66 if (s_first
>= 0 && s_first
<= s_last
) {
67 instr
->state
= MTD_ERASING
;
70 error
= flash_erase(fi
, s_first
, s_last
);
74 instr
->state
= MTD_ERASE_FAILED
;
78 instr
->state
= MTD_ERASE_DONE
;
79 mtd_erase_callback(instr
);
86 static int cfi_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
87 size_t *retlen
, u_char
*buf
)
89 flash_info_t
*fi
= mtd
->priv
;
90 u_char
*f
= (u_char
*)(fi
->start
[0]) + from
;
98 static int cfi_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
99 size_t *retlen
, const u_char
*buf
)
101 flash_info_t
*fi
= mtd
->priv
;
102 u_long t
= fi
->start
[0] + to
;
105 flash_set_verbose(0);
106 error
= write_buff(fi
, (u_char
*)buf
, t
, len
);
107 flash_set_verbose(1);
117 static void cfi_mtd_sync(struct mtd_info
*mtd
)
120 * This function should wait until all pending operations
121 * finish. However this driver is fully synchronous, so
122 * this function returns immediately
126 static int cfi_mtd_lock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
128 flash_info_t
*fi
= mtd
->priv
;
130 flash_set_verbose(0);
131 flash_protect(FLAG_PROTECT_SET
, fi
->start
[0] + ofs
,
132 fi
->start
[0] + ofs
+ len
- 1, fi
);
133 flash_set_verbose(1);
138 static int cfi_mtd_unlock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
140 flash_info_t
*fi
= mtd
->priv
;
142 flash_set_verbose(0);
143 flash_protect(FLAG_PROTECT_CLEAR
, fi
->start
[0] + ofs
,
144 fi
->start
[0] + ofs
+ len
- 1, fi
);
145 flash_set_verbose(1);
150 static int cfi_mtd_set_erasesize(struct mtd_info
*mtd
, flash_info_t
*fi
)
153 int sect_size_old
= 0;
158 ulong base_addr
= fi
->start
[0];
161 * First detect the number of eraseregions so that we can allocate
162 * the array of eraseregions correctly
164 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
165 if (sect_size_old
!= flash_sector_size(fi
, sect
))
167 sect_size_old
= flash_sector_size(fi
, sect
);
170 mtd
->eraseregions
= malloc(sizeof(struct mtd_erase_region_info
) * regions
);
173 * Now detect the largest sector and fill the eraseregions
177 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
178 if ((sect_size_old
!= flash_sector_size(fi
, sect
)) &&
179 (sect_size_old
!= 0)) {
180 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
181 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
182 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
184 /* Now start counting the next eraseregions */
191 if (sect_size_old
!= flash_sector_size(fi
, sect
))
192 offset
= fi
->start
[sect
];
195 * Select the largest sector size as erasesize (e.g. for UBI)
197 if (flash_sector_size(fi
, sect
) > sect_size
)
198 sect_size
= flash_sector_size(fi
, sect
);
200 sect_size_old
= flash_sector_size(fi
, sect
);
204 * Set the last region
206 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
207 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
208 mtd
->eraseregions
[regions
].numblocks
= numblocks
+ 1;
211 mtd
->numeraseregions
= regions
+ 1;
213 mtd
->numeraseregions
= 0;
215 mtd
->erasesize
= sect_size
;
220 int cfi_mtd_init(void)
222 struct mtd_info
*mtd
;
225 int devices_found
= 0;
226 struct mtd_info
*mtd_list
[CONFIG_SYS_MAX_FLASH_BANKS
];
228 for (i
= 0; i
< CONFIG_SYS_MAX_FLASH_BANKS
; i
++) {
230 mtd
= &cfi_mtd_info
[i
];
232 memset(mtd
, 0, sizeof(struct mtd_info
));
234 error
= cfi_mtd_set_erasesize(mtd
, fi
);
238 sprintf(cfi_mtd_names
[i
], "nor%d", i
);
239 mtd
->name
= cfi_mtd_names
[i
];
240 mtd
->type
= MTD_NORFLASH
;
241 mtd
->flags
= MTD_CAP_NORFLASH
;
242 mtd
->size
= fi
->size
;
245 mtd
->erase
= cfi_mtd_erase
;
246 mtd
->read
= cfi_mtd_read
;
247 mtd
->write
= cfi_mtd_write
;
248 mtd
->sync
= cfi_mtd_sync
;
249 mtd
->lock
= cfi_mtd_lock
;
250 mtd
->unlock
= cfi_mtd_unlock
;
253 if (add_mtd_device(mtd
))
256 mtd_list
[devices_found
++] = mtd
;
259 #ifdef CONFIG_MTD_CONCAT
260 if (devices_found
> 1) {
262 * We detected multiple devices. Concatenate them together.
264 sprintf(c_mtd_name
, "nor%d", devices_found
);
265 mtd
= mtd_concat_create(mtd_list
, devices_found
, c_mtd_name
);
270 if (add_mtd_device(mtd
))
273 #endif /* CONFIG_MTD_CONCAT */