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>
33 #include <mtd/cfi_flash.h>
35 static struct mtd_info cfi_mtd_info
[CFI_MAX_FLASH_BANKS
];
36 static char cfi_mtd_names
[CFI_MAX_FLASH_BANKS
][16];
37 #ifdef CONFIG_MTD_CONCAT
38 static char c_mtd_name
[16];
41 static int cfi_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
43 flash_info_t
*fi
= mtd
->priv
;
44 size_t a_start
= fi
->start
[0] + instr
->addr
;
45 size_t a_end
= a_start
+ instr
->len
;
50 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
51 if (a_start
== fi
->start
[sect
])
54 if (sect
< fi
->sector_count
- 1) {
55 if (a_end
== fi
->start
[sect
+ 1]) {
65 if (s_first
>= 0 && s_first
<= s_last
) {
66 instr
->state
= MTD_ERASING
;
69 error
= flash_erase(fi
, s_first
, s_last
);
73 instr
->state
= MTD_ERASE_FAILED
;
77 instr
->state
= MTD_ERASE_DONE
;
78 mtd_erase_callback(instr
);
85 static int cfi_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
86 size_t *retlen
, u_char
*buf
)
88 flash_info_t
*fi
= mtd
->priv
;
89 u_char
*f
= (u_char
*)(fi
->start
[0]) + from
;
97 static int cfi_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
98 size_t *retlen
, const u_char
*buf
)
100 flash_info_t
*fi
= mtd
->priv
;
101 u_long t
= fi
->start
[0] + to
;
104 flash_set_verbose(0);
105 error
= write_buff(fi
, (u_char
*)buf
, t
, len
);
106 flash_set_verbose(1);
116 static void cfi_mtd_sync(struct mtd_info
*mtd
)
119 * This function should wait until all pending operations
120 * finish. However this driver is fully synchronous, so
121 * this function returns immediately
125 static int cfi_mtd_lock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
127 flash_info_t
*fi
= mtd
->priv
;
129 flash_set_verbose(0);
130 flash_protect(FLAG_PROTECT_SET
, fi
->start
[0] + ofs
,
131 fi
->start
[0] + ofs
+ len
- 1, fi
);
132 flash_set_verbose(1);
137 static int cfi_mtd_unlock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
139 flash_info_t
*fi
= mtd
->priv
;
141 flash_set_verbose(0);
142 flash_protect(FLAG_PROTECT_CLEAR
, fi
->start
[0] + ofs
,
143 fi
->start
[0] + ofs
+ len
- 1, fi
);
144 flash_set_verbose(1);
149 static int cfi_mtd_set_erasesize(struct mtd_info
*mtd
, flash_info_t
*fi
)
152 int sect_size_old
= 0;
160 * First detect the number of eraseregions so that we can allocate
161 * the array of eraseregions correctly
163 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
164 if (sect_size_old
!= flash_sector_size(fi
, sect
))
166 sect_size_old
= flash_sector_size(fi
, sect
);
172 case 1: /* flash has uniform erase size */
173 mtd
->numeraseregions
= 0;
174 mtd
->erasesize
= sect_size_old
;
178 mtd
->numeraseregions
= regions
;
179 mtd
->eraseregions
= malloc(sizeof(struct mtd_erase_region_info
) * regions
);
182 * Now detect the largest sector and fill the eraseregions
185 base_addr
= offset
= fi
->start
[0];
186 sect_size_old
= flash_sector_size(fi
, 0);
187 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
188 if (sect_size_old
!= flash_sector_size(fi
, sect
)) {
189 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
190 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
191 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
192 /* Now start counting the next eraseregions */
195 offset
= fi
->start
[sect
];
200 * Select the largest sector size as erasesize (e.g. for UBI)
202 if (flash_sector_size(fi
, sect
) > sect_size
)
203 sect_size
= flash_sector_size(fi
, sect
);
205 sect_size_old
= flash_sector_size(fi
, sect
);
209 * Set the last region
211 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
212 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
213 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
215 mtd
->erasesize
= sect_size
;
220 int cfi_mtd_init(void)
222 struct mtd_info
*mtd
;
225 #ifdef CONFIG_MTD_CONCAT
226 int devices_found
= 0;
227 struct mtd_info
*mtd_list
[CONFIG_SYS_MAX_FLASH_BANKS
];
230 for (i
= 0; i
< CONFIG_SYS_MAX_FLASH_BANKS
; i
++) {
232 mtd
= &cfi_mtd_info
[i
];
234 memset(mtd
, 0, sizeof(struct mtd_info
));
236 error
= cfi_mtd_set_erasesize(mtd
, fi
);
240 sprintf(cfi_mtd_names
[i
], "nor%d", i
);
241 mtd
->name
= cfi_mtd_names
[i
];
242 mtd
->type
= MTD_NORFLASH
;
243 mtd
->flags
= MTD_CAP_NORFLASH
;
244 mtd
->size
= fi
->size
;
247 mtd
->erase
= cfi_mtd_erase
;
248 mtd
->read
= cfi_mtd_read
;
249 mtd
->write
= cfi_mtd_write
;
250 mtd
->sync
= cfi_mtd_sync
;
251 mtd
->lock
= cfi_mtd_lock
;
252 mtd
->unlock
= cfi_mtd_unlock
;
255 if (add_mtd_device(mtd
))
258 #ifdef CONFIG_MTD_CONCAT
259 mtd_list
[devices_found
++] = mtd
;
263 #ifdef CONFIG_MTD_CONCAT
264 if (devices_found
> 1) {
266 * We detected multiple devices. Concatenate them together.
268 sprintf(c_mtd_name
, "nor%d", devices_found
);
269 mtd
= mtd_concat_create(mtd_list
, devices_found
, c_mtd_name
);
274 if (add_mtd_device(mtd
))
277 #endif /* CONFIG_MTD_CONCAT */