#include <common.h>
#include <flash.h>
+#include <malloc.h>
#include <asm/errno.h>
#include <linux/mtd/mtd.h>
+#include <linux/mtd/concat.h>
+
+/* use CONFIG_SYS_MAX_FLASH_BANKS_DETECT if defined */
+#ifdef CONFIG_SYS_MAX_FLASH_BANKS_DETECT
+# define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS_DETECT
+#else
+# define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS
+#endif
extern flash_info_t flash_info[];
-static struct mtd_info cfi_mtd_info[CONFIG_SYS_MAX_FLASH_BANKS];
-static char cfi_mtd_names[CONFIG_SYS_MAX_FLASH_BANKS][16];
+static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS];
+static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16];
+#ifdef CONFIG_MTD_CONCAT
+static char c_mtd_name[16];
+#endif
static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
int s_last = -1;
int error, sect;
- for (sect = 0; sect < fi->sector_count - 1; sect++) {
+ for (sect = 0; sect < fi->sector_count; sect++) {
if (a_start == fi->start[sect])
s_first = sect;
- if (a_end == fi->start[sect + 1]) {
+ if (sect < fi->sector_count - 1) {
+ if (a_end == fi->start[sect + 1]) {
+ s_last = sect;
+ break;
+ }
+ } else {
s_last = sect;
break;
}
*/
}
-static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
flash_info_t *fi = mtd->priv;
return 0;
}
-static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
flash_info_t *fi = mtd->priv;
static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
{
int sect_size = 0;
+ int sect_size_old = 0;
int sect;
+ int regions = 0;
+ int numblocks = 0;
+ ulong offset;
+ ulong base_addr;
+ /*
+ * First detect the number of eraseregions so that we can allocate
+ * the array of eraseregions correctly
+ */
for (sect = 0; sect < fi->sector_count; sect++) {
- if (!sect_size) {
- sect_size = flash_sector_size(fi, sect);
- continue;
- }
+ if (sect_size_old != flash_sector_size(fi, sect))
+ regions++;
+ sect_size_old = flash_sector_size(fi, sect);
+ }
- if (sect_size != flash_sector_size(fi, sect)) {
- sect_size = 0;
- break;
- }
+ switch (regions) {
+ case 0:
+ return 1;
+ case 1: /* flash has uniform erase size */
+ mtd->numeraseregions = 0;
+ mtd->erasesize = sect_size_old;
+ return 0;
}
- if (!sect_size) {
- puts("cfi-mtd: devices with multiple sector sizes are"
- "not supported\n");
- return -EINVAL;
+ mtd->numeraseregions = regions;
+ mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions);
+
+ /*
+ * Now detect the largest sector and fill the eraseregions
+ */
+ regions = 0;
+ base_addr = offset = fi->start[0];
+ sect_size_old = flash_sector_size(fi, 0);
+ for (sect = 0; sect < fi->sector_count; sect++) {
+ if (sect_size_old != flash_sector_size(fi, sect)) {
+ mtd->eraseregions[regions].offset = offset - base_addr;
+ mtd->eraseregions[regions].erasesize = sect_size_old;
+ mtd->eraseregions[regions].numblocks = numblocks;
+ /* Now start counting the next eraseregions */
+ numblocks = 0;
+ regions++;
+ offset = fi->start[sect];
+ }
+ numblocks++;
+
+ /*
+ * Select the largest sector size as erasesize (e.g. for UBI)
+ */
+ if (flash_sector_size(fi, sect) > sect_size)
+ sect_size = flash_sector_size(fi, sect);
+
+ sect_size_old = flash_sector_size(fi, sect);
}
+ /*
+ * Set the last region
+ */
+ mtd->eraseregions[regions].offset = offset - base_addr;
+ mtd->eraseregions[regions].erasesize = sect_size_old;
+ mtd->eraseregions[regions].numblocks = numblocks;
+
mtd->erasesize = sect_size;
return 0;
struct mtd_info *mtd;
flash_info_t *fi;
int error, i;
+ int devices_found = 0;
+ struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS];
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
fi = &flash_info[i];
if (add_mtd_device(mtd))
return -ENOMEM;
+
+ mtd_list[devices_found++] = mtd;
+ }
+
+#ifdef CONFIG_MTD_CONCAT
+ if (devices_found > 1) {
+ /*
+ * We detected multiple devices. Concatenate them together.
+ */
+ sprintf(c_mtd_name, "nor%d", devices_found);
+ mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name);
+
+ if (mtd == NULL)
+ return -ENXIO;
+
+ if (add_mtd_device(mtd))
+ return -ENOMEM;
}
+#endif /* CONFIG_MTD_CONCAT */
return 0;
}