191809ed5SPiotr Ziecik /* 291809ed5SPiotr Ziecik * (C) Copyright 2008 Semihalf 391809ed5SPiotr Ziecik * 491809ed5SPiotr Ziecik * Written by: Piotr Ziecik <kosmo@semihalf.com> 591809ed5SPiotr Ziecik * 691809ed5SPiotr Ziecik * See file CREDITS for list of people who contributed to this 791809ed5SPiotr Ziecik * project. 891809ed5SPiotr Ziecik * 991809ed5SPiotr Ziecik * This program is free software; you can redistribute it and/or 1091809ed5SPiotr Ziecik * modify it under the terms of the GNU General Public License as 1191809ed5SPiotr Ziecik * published by the Free Software Foundation; either version 2 of 1291809ed5SPiotr Ziecik * the License, or (at your option) any later version. 1391809ed5SPiotr Ziecik * 1491809ed5SPiotr Ziecik * This program is distributed in the hope that it will be useful, 1591809ed5SPiotr Ziecik * but WITHOUT ANY WARRANTY; without even the implied warranty of 1691809ed5SPiotr Ziecik * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1791809ed5SPiotr Ziecik * GNU General Public License for more details. 1891809ed5SPiotr Ziecik * 1991809ed5SPiotr Ziecik * You should have received a copy of the GNU General Public License 2091809ed5SPiotr Ziecik * along with this program; if not, write to the Free Software 2191809ed5SPiotr Ziecik * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2291809ed5SPiotr Ziecik * MA 02111-1307 USA 2391809ed5SPiotr Ziecik * 2491809ed5SPiotr Ziecik */ 2591809ed5SPiotr Ziecik 2691809ed5SPiotr Ziecik #include <common.h> 2791809ed5SPiotr Ziecik #include <flash.h> 280a572655SStefan Roese #include <malloc.h> 2991809ed5SPiotr Ziecik 3091809ed5SPiotr Ziecik #include <asm/errno.h> 3191809ed5SPiotr Ziecik #include <linux/mtd/mtd.h> 320a572655SStefan Roese #include <linux/mtd/concat.h> 3391809ed5SPiotr Ziecik 34*9578718cSKim Phillips /* use CONFIG_SYS_MAX_FLASH_BANKS_DETECT if defined */ 35*9578718cSKim Phillips #ifdef CONFIG_SYS_MAX_FLASH_BANKS_DETECT 36*9578718cSKim Phillips # define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS_DETECT 37*9578718cSKim Phillips #else 38*9578718cSKim Phillips # define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS 39*9578718cSKim Phillips #endif 40*9578718cSKim Phillips 4191809ed5SPiotr Ziecik extern flash_info_t flash_info[]; 4291809ed5SPiotr Ziecik 43*9578718cSKim Phillips static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS]; 44*9578718cSKim Phillips static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16]; 450a572655SStefan Roese #ifdef CONFIG_MTD_CONCAT 460a572655SStefan Roese static char c_mtd_name[16]; 470a572655SStefan Roese #endif 4891809ed5SPiotr Ziecik 4991809ed5SPiotr Ziecik static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 5091809ed5SPiotr Ziecik { 5191809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 5291809ed5SPiotr Ziecik size_t a_start = fi->start[0] + instr->addr; 5391809ed5SPiotr Ziecik size_t a_end = a_start + instr->len; 5491809ed5SPiotr Ziecik int s_first = -1; 5591809ed5SPiotr Ziecik int s_last = -1; 5691809ed5SPiotr Ziecik int error, sect; 5791809ed5SPiotr Ziecik 58dba6fcf6SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 5991809ed5SPiotr Ziecik if (a_start == fi->start[sect]) 6091809ed5SPiotr Ziecik s_first = sect; 6191809ed5SPiotr Ziecik 62dba6fcf6SStefan Roese if (sect < fi->sector_count - 1) { 6391809ed5SPiotr Ziecik if (a_end == fi->start[sect + 1]) { 6491809ed5SPiotr Ziecik s_last = sect; 6591809ed5SPiotr Ziecik break; 6691809ed5SPiotr Ziecik } 67dba6fcf6SStefan Roese } else { 68dba6fcf6SStefan Roese s_last = sect; 69dba6fcf6SStefan Roese break; 70dba6fcf6SStefan Roese } 7191809ed5SPiotr Ziecik } 7291809ed5SPiotr Ziecik 7391809ed5SPiotr Ziecik if (s_first >= 0 && s_first <= s_last) { 7491809ed5SPiotr Ziecik instr->state = MTD_ERASING; 7591809ed5SPiotr Ziecik 7691809ed5SPiotr Ziecik flash_set_verbose(0); 7791809ed5SPiotr Ziecik error = flash_erase(fi, s_first, s_last); 7891809ed5SPiotr Ziecik flash_set_verbose(1); 7991809ed5SPiotr Ziecik 8091809ed5SPiotr Ziecik if (error) { 8191809ed5SPiotr Ziecik instr->state = MTD_ERASE_FAILED; 8291809ed5SPiotr Ziecik return -EIO; 8391809ed5SPiotr Ziecik } 8491809ed5SPiotr Ziecik 8591809ed5SPiotr Ziecik instr->state = MTD_ERASE_DONE; 8691809ed5SPiotr Ziecik mtd_erase_callback(instr); 8791809ed5SPiotr Ziecik return 0; 8891809ed5SPiotr Ziecik } 8991809ed5SPiotr Ziecik 9091809ed5SPiotr Ziecik return -EINVAL; 9191809ed5SPiotr Ziecik } 9291809ed5SPiotr Ziecik 9391809ed5SPiotr Ziecik static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 9491809ed5SPiotr Ziecik size_t *retlen, u_char *buf) 9591809ed5SPiotr Ziecik { 9691809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 9791809ed5SPiotr Ziecik u_char *f = (u_char*)(fi->start[0]) + from; 9891809ed5SPiotr Ziecik 9991809ed5SPiotr Ziecik memcpy(buf, f, len); 10091809ed5SPiotr Ziecik *retlen = len; 10191809ed5SPiotr Ziecik 10291809ed5SPiotr Ziecik return 0; 10391809ed5SPiotr Ziecik } 10491809ed5SPiotr Ziecik 10591809ed5SPiotr Ziecik static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 10691809ed5SPiotr Ziecik size_t *retlen, const u_char *buf) 10791809ed5SPiotr Ziecik { 10891809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 10991809ed5SPiotr Ziecik u_long t = fi->start[0] + to; 11091809ed5SPiotr Ziecik int error; 11191809ed5SPiotr Ziecik 11291809ed5SPiotr Ziecik flash_set_verbose(0); 11391809ed5SPiotr Ziecik error = write_buff(fi, (u_char*)buf, t, len); 11491809ed5SPiotr Ziecik flash_set_verbose(1); 11591809ed5SPiotr Ziecik 11691809ed5SPiotr Ziecik if (!error) { 11791809ed5SPiotr Ziecik *retlen = len; 11891809ed5SPiotr Ziecik return 0; 11991809ed5SPiotr Ziecik } 12091809ed5SPiotr Ziecik 12191809ed5SPiotr Ziecik return -EIO; 12291809ed5SPiotr Ziecik } 12391809ed5SPiotr Ziecik 12491809ed5SPiotr Ziecik static void cfi_mtd_sync(struct mtd_info *mtd) 12591809ed5SPiotr Ziecik { 12691809ed5SPiotr Ziecik /* 12791809ed5SPiotr Ziecik * This function should wait until all pending operations 12891809ed5SPiotr Ziecik * finish. However this driver is fully synchronous, so 12991809ed5SPiotr Ziecik * this function returns immediately 13091809ed5SPiotr Ziecik */ 13191809ed5SPiotr Ziecik } 13291809ed5SPiotr Ziecik 1338d2effeaSStefan Roese static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 13491809ed5SPiotr Ziecik { 13591809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 13691809ed5SPiotr Ziecik 13791809ed5SPiotr Ziecik flash_set_verbose(0); 13891809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs, 13991809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 14091809ed5SPiotr Ziecik flash_set_verbose(1); 14191809ed5SPiotr Ziecik 14291809ed5SPiotr Ziecik return 0; 14391809ed5SPiotr Ziecik } 14491809ed5SPiotr Ziecik 1458d2effeaSStefan Roese static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 14691809ed5SPiotr Ziecik { 14791809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 14891809ed5SPiotr Ziecik 14991809ed5SPiotr Ziecik flash_set_verbose(0); 15091809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs, 15191809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 15291809ed5SPiotr Ziecik flash_set_verbose(1); 15391809ed5SPiotr Ziecik 15491809ed5SPiotr Ziecik return 0; 15591809ed5SPiotr Ziecik } 15691809ed5SPiotr Ziecik 15791809ed5SPiotr Ziecik static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) 15891809ed5SPiotr Ziecik { 15991809ed5SPiotr Ziecik int sect_size = 0; 1600a572655SStefan Roese int sect_size_old = 0; 16191809ed5SPiotr Ziecik int sect; 1620a572655SStefan Roese int regions = 0; 1630a572655SStefan Roese int numblocks = 0; 1640a572655SStefan Roese ulong offset = 0; 1650a572655SStefan Roese ulong base_addr = fi->start[0]; 1660a572655SStefan Roese 1670a572655SStefan Roese /* 1680a572655SStefan Roese * First detect the number of eraseregions so that we can allocate 1690a572655SStefan Roese * the array of eraseregions correctly 1700a572655SStefan Roese */ 1710a572655SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 1720a572655SStefan Roese if (sect_size_old != flash_sector_size(fi, sect)) 1730a572655SStefan Roese regions++; 1740a572655SStefan Roese sect_size_old = flash_sector_size(fi, sect); 1750a572655SStefan Roese } 1760a572655SStefan Roese 1770a572655SStefan Roese mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions); 1780a572655SStefan Roese 1790a572655SStefan Roese /* 1800a572655SStefan Roese * Now detect the largest sector and fill the eraseregions 1810a572655SStefan Roese */ 1820a572655SStefan Roese sect_size_old = 0; 1830a572655SStefan Roese regions = 0; 1840a572655SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 1850a572655SStefan Roese if ((sect_size_old != flash_sector_size(fi, sect)) && 1860a572655SStefan Roese (sect_size_old != 0)) { 1870a572655SStefan Roese mtd->eraseregions[regions].offset = offset - base_addr; 1880a572655SStefan Roese mtd->eraseregions[regions].erasesize = sect_size_old; 1890a572655SStefan Roese mtd->eraseregions[regions].numblocks = numblocks; 1900a572655SStefan Roese 1910a572655SStefan Roese /* Now start counting the next eraseregions */ 1920a572655SStefan Roese numblocks = 0; 1930a572655SStefan Roese regions++; 1940a572655SStefan Roese } else { 1950a572655SStefan Roese numblocks++; 1960a572655SStefan Roese } 1970a572655SStefan Roese 1980a572655SStefan Roese if (sect_size_old != flash_sector_size(fi, sect)) 1990a572655SStefan Roese offset = fi->start[sect]; 20091809ed5SPiotr Ziecik 201f8e2b310SStefan Roese /* 202f8e2b310SStefan Roese * Select the largest sector size as erasesize (e.g. for UBI) 203f8e2b310SStefan Roese */ 204f8e2b310SStefan Roese if (flash_sector_size(fi, sect) > sect_size) 20591809ed5SPiotr Ziecik sect_size = flash_sector_size(fi, sect); 2060a572655SStefan Roese 2070a572655SStefan Roese sect_size_old = flash_sector_size(fi, sect); 20891809ed5SPiotr Ziecik } 20991809ed5SPiotr Ziecik 2100a572655SStefan Roese /* 2110a572655SStefan Roese * Set the last region 2120a572655SStefan Roese */ 2130a572655SStefan Roese mtd->eraseregions[regions].offset = offset - base_addr; 2140a572655SStefan Roese mtd->eraseregions[regions].erasesize = sect_size_old; 2150a572655SStefan Roese mtd->eraseregions[regions].numblocks = numblocks + 1; 2160a572655SStefan Roese 2170a572655SStefan Roese if (regions) 2180a572655SStefan Roese mtd->numeraseregions = regions + 1; 2190a572655SStefan Roese else 2200a572655SStefan Roese mtd->numeraseregions = 0; 2210a572655SStefan Roese 22291809ed5SPiotr Ziecik mtd->erasesize = sect_size; 22391809ed5SPiotr Ziecik 22491809ed5SPiotr Ziecik return 0; 22591809ed5SPiotr Ziecik } 22691809ed5SPiotr Ziecik 22791809ed5SPiotr Ziecik int cfi_mtd_init(void) 22891809ed5SPiotr Ziecik { 22991809ed5SPiotr Ziecik struct mtd_info *mtd; 23091809ed5SPiotr Ziecik flash_info_t *fi; 23191809ed5SPiotr Ziecik int error, i; 2320a572655SStefan Roese int devices_found = 0; 2330a572655SStefan Roese struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS]; 23491809ed5SPiotr Ziecik 23591809ed5SPiotr Ziecik for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 23691809ed5SPiotr Ziecik fi = &flash_info[i]; 23791809ed5SPiotr Ziecik mtd = &cfi_mtd_info[i]; 23891809ed5SPiotr Ziecik 23991809ed5SPiotr Ziecik memset(mtd, 0, sizeof(struct mtd_info)); 24091809ed5SPiotr Ziecik 24191809ed5SPiotr Ziecik error = cfi_mtd_set_erasesize(mtd, fi); 24291809ed5SPiotr Ziecik if (error) 24391809ed5SPiotr Ziecik continue; 24491809ed5SPiotr Ziecik 245c203ef5dSAndreas Huber sprintf(cfi_mtd_names[i], "nor%d", i); 246c203ef5dSAndreas Huber mtd->name = cfi_mtd_names[i]; 24791809ed5SPiotr Ziecik mtd->type = MTD_NORFLASH; 24891809ed5SPiotr Ziecik mtd->flags = MTD_CAP_NORFLASH; 24991809ed5SPiotr Ziecik mtd->size = fi->size; 25091809ed5SPiotr Ziecik mtd->writesize = 1; 25191809ed5SPiotr Ziecik 25291809ed5SPiotr Ziecik mtd->erase = cfi_mtd_erase; 25391809ed5SPiotr Ziecik mtd->read = cfi_mtd_read; 25491809ed5SPiotr Ziecik mtd->write = cfi_mtd_write; 25591809ed5SPiotr Ziecik mtd->sync = cfi_mtd_sync; 25691809ed5SPiotr Ziecik mtd->lock = cfi_mtd_lock; 25791809ed5SPiotr Ziecik mtd->unlock = cfi_mtd_unlock; 25891809ed5SPiotr Ziecik mtd->priv = fi; 25991809ed5SPiotr Ziecik 26091809ed5SPiotr Ziecik if (add_mtd_device(mtd)) 26191809ed5SPiotr Ziecik return -ENOMEM; 2620a572655SStefan Roese 2630a572655SStefan Roese mtd_list[devices_found++] = mtd; 26491809ed5SPiotr Ziecik } 26591809ed5SPiotr Ziecik 2660a572655SStefan Roese #ifdef CONFIG_MTD_CONCAT 2670a572655SStefan Roese if (devices_found > 1) { 2680a572655SStefan Roese /* 2690a572655SStefan Roese * We detected multiple devices. Concatenate them together. 2700a572655SStefan Roese */ 2710a572655SStefan Roese sprintf(c_mtd_name, "nor%d", devices_found); 2720a572655SStefan Roese mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name); 2730a572655SStefan Roese 2740a572655SStefan Roese if (mtd == NULL) 2750a572655SStefan Roese return -ENXIO; 2760a572655SStefan Roese 2770a572655SStefan Roese if (add_mtd_device(mtd)) 2780a572655SStefan Roese return -ENOMEM; 2790a572655SStefan Roese } 2800a572655SStefan Roese #endif /* CONFIG_MTD_CONCAT */ 2810a572655SStefan Roese 28291809ed5SPiotr Ziecik return 0; 28391809ed5SPiotr Ziecik } 284