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 /* use CONFIG_SYS_MAX_FLASH_BANKS_DETECT if defined */
35 #ifdef CONFIG_SYS_MAX_FLASH_BANKS_DETECT
36 # define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS_DETECT
38 # define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS
41 extern flash_info_t flash_info
[];
43 static struct mtd_info cfi_mtd_info
[CFI_MAX_FLASH_BANKS
];
44 static char cfi_mtd_names
[CFI_MAX_FLASH_BANKS
][16];
45 #ifdef CONFIG_MTD_CONCAT
46 static char c_mtd_name
[16];
49 static int cfi_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
51 flash_info_t
*fi
= mtd
->priv
;
52 size_t a_start
= fi
->start
[0] + instr
->addr
;
53 size_t a_end
= a_start
+ instr
->len
;
58 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
59 if (a_start
== fi
->start
[sect
])
62 if (sect
< fi
->sector_count
- 1) {
63 if (a_end
== fi
->start
[sect
+ 1]) {
73 if (s_first
>= 0 && s_first
<= s_last
) {
74 instr
->state
= MTD_ERASING
;
77 error
= flash_erase(fi
, s_first
, s_last
);
81 instr
->state
= MTD_ERASE_FAILED
;
85 instr
->state
= MTD_ERASE_DONE
;
86 mtd_erase_callback(instr
);
93 static int cfi_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
94 size_t *retlen
, u_char
*buf
)
96 flash_info_t
*fi
= mtd
->priv
;
97 u_char
*f
= (u_char
*)(fi
->start
[0]) + from
;
105 static int cfi_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
106 size_t *retlen
, const u_char
*buf
)
108 flash_info_t
*fi
= mtd
->priv
;
109 u_long t
= fi
->start
[0] + to
;
112 flash_set_verbose(0);
113 error
= write_buff(fi
, (u_char
*)buf
, t
, len
);
114 flash_set_verbose(1);
124 static void cfi_mtd_sync(struct mtd_info
*mtd
)
127 * This function should wait until all pending operations
128 * finish. However this driver is fully synchronous, so
129 * this function returns immediately
133 static int cfi_mtd_lock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
135 flash_info_t
*fi
= mtd
->priv
;
137 flash_set_verbose(0);
138 flash_protect(FLAG_PROTECT_SET
, fi
->start
[0] + ofs
,
139 fi
->start
[0] + ofs
+ len
- 1, fi
);
140 flash_set_verbose(1);
145 static int cfi_mtd_unlock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
147 flash_info_t
*fi
= mtd
->priv
;
149 flash_set_verbose(0);
150 flash_protect(FLAG_PROTECT_CLEAR
, fi
->start
[0] + ofs
,
151 fi
->start
[0] + ofs
+ len
- 1, fi
);
152 flash_set_verbose(1);
157 static int cfi_mtd_set_erasesize(struct mtd_info
*mtd
, flash_info_t
*fi
)
160 int sect_size_old
= 0;
165 ulong base_addr
= fi
->start
[0];
168 * First detect the number of eraseregions so that we can allocate
169 * the array of eraseregions correctly
171 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
172 if (sect_size_old
!= flash_sector_size(fi
, sect
))
174 sect_size_old
= flash_sector_size(fi
, sect
);
177 mtd
->eraseregions
= malloc(sizeof(struct mtd_erase_region_info
) * regions
);
180 * Now detect the largest sector and fill the eraseregions
184 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
185 if ((sect_size_old
!= flash_sector_size(fi
, sect
)) &&
186 (sect_size_old
!= 0)) {
187 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
188 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
189 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
191 /* Now start counting the next eraseregions */
198 if (sect_size_old
!= flash_sector_size(fi
, sect
))
199 offset
= fi
->start
[sect
];
202 * Select the largest sector size as erasesize (e.g. for UBI)
204 if (flash_sector_size(fi
, sect
) > sect_size
)
205 sect_size
= flash_sector_size(fi
, sect
);
207 sect_size_old
= flash_sector_size(fi
, sect
);
211 * Set the last region
213 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
214 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
215 mtd
->eraseregions
[regions
].numblocks
= numblocks
+ 1;
218 mtd
->numeraseregions
= regions
+ 1;
220 mtd
->numeraseregions
= 0;
222 mtd
->erasesize
= sect_size
;
227 int cfi_mtd_init(void)
229 struct mtd_info
*mtd
;
232 int devices_found
= 0;
233 struct mtd_info
*mtd_list
[CONFIG_SYS_MAX_FLASH_BANKS
];
235 for (i
= 0; i
< CONFIG_SYS_MAX_FLASH_BANKS
; i
++) {
237 mtd
= &cfi_mtd_info
[i
];
239 memset(mtd
, 0, sizeof(struct mtd_info
));
241 error
= cfi_mtd_set_erasesize(mtd
, fi
);
245 sprintf(cfi_mtd_names
[i
], "nor%d", i
);
246 mtd
->name
= cfi_mtd_names
[i
];
247 mtd
->type
= MTD_NORFLASH
;
248 mtd
->flags
= MTD_CAP_NORFLASH
;
249 mtd
->size
= fi
->size
;
252 mtd
->erase
= cfi_mtd_erase
;
253 mtd
->read
= cfi_mtd_read
;
254 mtd
->write
= cfi_mtd_write
;
255 mtd
->sync
= cfi_mtd_sync
;
256 mtd
->lock
= cfi_mtd_lock
;
257 mtd
->unlock
= cfi_mtd_unlock
;
260 if (add_mtd_device(mtd
))
263 mtd_list
[devices_found
++] = mtd
;
266 #ifdef CONFIG_MTD_CONCAT
267 if (devices_found
> 1) {
269 * We detected multiple devices. Concatenate them together.
271 sprintf(c_mtd_name
, "nor%d", devices_found
);
272 mtd
= mtd_concat_create(mtd_list
, devices_found
, c_mtd_name
);
277 if (add_mtd_device(mtd
))
280 #endif /* CONFIG_MTD_CONCAT */