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> 333c29975eSStefan Roese #include <mtd/cfi_flash.h> 3491809ed5SPiotr Ziecik 359578718cSKim Phillips static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS]; 369578718cSKim Phillips static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16]; 370a572655SStefan Roese #ifdef CONFIG_MTD_CONCAT 380a572655SStefan Roese static char c_mtd_name[16]; 390a572655SStefan Roese #endif 4091809ed5SPiotr Ziecik 4191809ed5SPiotr Ziecik static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 4291809ed5SPiotr Ziecik { 4391809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 4491809ed5SPiotr Ziecik size_t a_start = fi->start[0] + instr->addr; 4591809ed5SPiotr Ziecik size_t a_end = a_start + instr->len; 4691809ed5SPiotr Ziecik int s_first = -1; 4791809ed5SPiotr Ziecik int s_last = -1; 4891809ed5SPiotr Ziecik int error, sect; 4991809ed5SPiotr Ziecik 50dba6fcf6SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 5191809ed5SPiotr Ziecik if (a_start == fi->start[sect]) 5291809ed5SPiotr Ziecik s_first = sect; 5391809ed5SPiotr Ziecik 54dba6fcf6SStefan Roese if (sect < fi->sector_count - 1) { 5591809ed5SPiotr Ziecik if (a_end == fi->start[sect + 1]) { 5691809ed5SPiotr Ziecik s_last = sect; 5791809ed5SPiotr Ziecik break; 5891809ed5SPiotr Ziecik } 59dba6fcf6SStefan Roese } else { 60dba6fcf6SStefan Roese s_last = sect; 61dba6fcf6SStefan Roese break; 62dba6fcf6SStefan Roese } 6391809ed5SPiotr Ziecik } 6491809ed5SPiotr Ziecik 6591809ed5SPiotr Ziecik if (s_first >= 0 && s_first <= s_last) { 6691809ed5SPiotr Ziecik instr->state = MTD_ERASING; 6791809ed5SPiotr Ziecik 6891809ed5SPiotr Ziecik flash_set_verbose(0); 6991809ed5SPiotr Ziecik error = flash_erase(fi, s_first, s_last); 7091809ed5SPiotr Ziecik flash_set_verbose(1); 7191809ed5SPiotr Ziecik 7291809ed5SPiotr Ziecik if (error) { 7391809ed5SPiotr Ziecik instr->state = MTD_ERASE_FAILED; 7491809ed5SPiotr Ziecik return -EIO; 7591809ed5SPiotr Ziecik } 7691809ed5SPiotr Ziecik 7791809ed5SPiotr Ziecik instr->state = MTD_ERASE_DONE; 7891809ed5SPiotr Ziecik mtd_erase_callback(instr); 7991809ed5SPiotr Ziecik return 0; 8091809ed5SPiotr Ziecik } 8191809ed5SPiotr Ziecik 8291809ed5SPiotr Ziecik return -EINVAL; 8391809ed5SPiotr Ziecik } 8491809ed5SPiotr Ziecik 8591809ed5SPiotr Ziecik static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 8691809ed5SPiotr Ziecik size_t *retlen, u_char *buf) 8791809ed5SPiotr Ziecik { 8891809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 8991809ed5SPiotr Ziecik u_char *f = (u_char*)(fi->start[0]) + from; 9091809ed5SPiotr Ziecik 9191809ed5SPiotr Ziecik memcpy(buf, f, len); 9291809ed5SPiotr Ziecik *retlen = len; 9391809ed5SPiotr Ziecik 9491809ed5SPiotr Ziecik return 0; 9591809ed5SPiotr Ziecik } 9691809ed5SPiotr Ziecik 9791809ed5SPiotr Ziecik static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 9891809ed5SPiotr Ziecik size_t *retlen, const u_char *buf) 9991809ed5SPiotr Ziecik { 10091809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 10191809ed5SPiotr Ziecik u_long t = fi->start[0] + to; 10291809ed5SPiotr Ziecik int error; 10391809ed5SPiotr Ziecik 10491809ed5SPiotr Ziecik flash_set_verbose(0); 10591809ed5SPiotr Ziecik error = write_buff(fi, (u_char*)buf, t, len); 10691809ed5SPiotr Ziecik flash_set_verbose(1); 10791809ed5SPiotr Ziecik 10891809ed5SPiotr Ziecik if (!error) { 10991809ed5SPiotr Ziecik *retlen = len; 11091809ed5SPiotr Ziecik return 0; 11191809ed5SPiotr Ziecik } 11291809ed5SPiotr Ziecik 11391809ed5SPiotr Ziecik return -EIO; 11491809ed5SPiotr Ziecik } 11591809ed5SPiotr Ziecik 11691809ed5SPiotr Ziecik static void cfi_mtd_sync(struct mtd_info *mtd) 11791809ed5SPiotr Ziecik { 11891809ed5SPiotr Ziecik /* 11991809ed5SPiotr Ziecik * This function should wait until all pending operations 12091809ed5SPiotr Ziecik * finish. However this driver is fully synchronous, so 12191809ed5SPiotr Ziecik * this function returns immediately 12291809ed5SPiotr Ziecik */ 12391809ed5SPiotr Ziecik } 12491809ed5SPiotr Ziecik 1258d2effeaSStefan Roese static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 12691809ed5SPiotr Ziecik { 12791809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 12891809ed5SPiotr Ziecik 12991809ed5SPiotr Ziecik flash_set_verbose(0); 13091809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs, 13191809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 13291809ed5SPiotr Ziecik flash_set_verbose(1); 13391809ed5SPiotr Ziecik 13491809ed5SPiotr Ziecik return 0; 13591809ed5SPiotr Ziecik } 13691809ed5SPiotr Ziecik 1378d2effeaSStefan Roese static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 13891809ed5SPiotr Ziecik { 13991809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 14091809ed5SPiotr Ziecik 14191809ed5SPiotr Ziecik flash_set_verbose(0); 14291809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs, 14391809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 14491809ed5SPiotr Ziecik flash_set_verbose(1); 14591809ed5SPiotr Ziecik 14691809ed5SPiotr Ziecik return 0; 14791809ed5SPiotr Ziecik } 14891809ed5SPiotr Ziecik 14991809ed5SPiotr Ziecik static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) 15091809ed5SPiotr Ziecik { 15191809ed5SPiotr Ziecik int sect_size = 0; 1520a572655SStefan Roese int sect_size_old = 0; 15391809ed5SPiotr Ziecik int sect; 1540a572655SStefan Roese int regions = 0; 1550a572655SStefan Roese int numblocks = 0; 156f3dec798SLadislav Michl ulong offset; 157f3dec798SLadislav Michl ulong base_addr; 1580a572655SStefan Roese 1590a572655SStefan Roese /* 1600a572655SStefan Roese * First detect the number of eraseregions so that we can allocate 1610a572655SStefan Roese * the array of eraseregions correctly 1620a572655SStefan Roese */ 1630a572655SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 1640a572655SStefan Roese if (sect_size_old != flash_sector_size(fi, sect)) 1650a572655SStefan Roese regions++; 1660a572655SStefan Roese sect_size_old = flash_sector_size(fi, sect); 1670a572655SStefan Roese } 1680a572655SStefan Roese 169f3dec798SLadislav Michl switch (regions) { 170f3dec798SLadislav Michl case 0: 171f3dec798SLadislav Michl return 1; 172f3dec798SLadislav Michl case 1: /* flash has uniform erase size */ 173f3dec798SLadislav Michl mtd->numeraseregions = 0; 174f3dec798SLadislav Michl mtd->erasesize = sect_size_old; 175f3dec798SLadislav Michl return 0; 176f3dec798SLadislav Michl } 177f3dec798SLadislav Michl 178f3dec798SLadislav Michl mtd->numeraseregions = regions; 1790a572655SStefan Roese mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions); 1800a572655SStefan Roese 1810a572655SStefan Roese /* 1820a572655SStefan Roese * Now detect the largest sector and fill the eraseregions 1830a572655SStefan Roese */ 1840a572655SStefan Roese regions = 0; 185f3dec798SLadislav Michl base_addr = offset = fi->start[0]; 186f3dec798SLadislav Michl sect_size_old = flash_sector_size(fi, 0); 1870a572655SStefan Roese for (sect = 0; sect < fi->sector_count; sect++) { 188f3dec798SLadislav Michl if (sect_size_old != flash_sector_size(fi, sect)) { 1890a572655SStefan Roese mtd->eraseregions[regions].offset = offset - base_addr; 1900a572655SStefan Roese mtd->eraseregions[regions].erasesize = sect_size_old; 1910a572655SStefan Roese mtd->eraseregions[regions].numblocks = numblocks; 1920a572655SStefan Roese /* Now start counting the next eraseregions */ 1930a572655SStefan Roese numblocks = 0; 1940a572655SStefan Roese regions++; 1950a572655SStefan Roese offset = fi->start[sect]; 196f3dec798SLadislav Michl } 197f3dec798SLadislav Michl numblocks++; 19891809ed5SPiotr Ziecik 199f8e2b310SStefan Roese /* 200f8e2b310SStefan Roese * Select the largest sector size as erasesize (e.g. for UBI) 201f8e2b310SStefan Roese */ 202f8e2b310SStefan Roese if (flash_sector_size(fi, sect) > sect_size) 20391809ed5SPiotr Ziecik sect_size = flash_sector_size(fi, sect); 2040a572655SStefan Roese 2050a572655SStefan Roese sect_size_old = flash_sector_size(fi, sect); 20691809ed5SPiotr Ziecik } 20791809ed5SPiotr Ziecik 2080a572655SStefan Roese /* 2090a572655SStefan Roese * Set the last region 2100a572655SStefan Roese */ 2110a572655SStefan Roese mtd->eraseregions[regions].offset = offset - base_addr; 2120a572655SStefan Roese mtd->eraseregions[regions].erasesize = sect_size_old; 213f3dec798SLadislav Michl mtd->eraseregions[regions].numblocks = numblocks; 2140a572655SStefan Roese 21591809ed5SPiotr Ziecik mtd->erasesize = sect_size; 21691809ed5SPiotr Ziecik 21791809ed5SPiotr Ziecik return 0; 21891809ed5SPiotr Ziecik } 21991809ed5SPiotr Ziecik 22091809ed5SPiotr Ziecik int cfi_mtd_init(void) 22191809ed5SPiotr Ziecik { 22291809ed5SPiotr Ziecik struct mtd_info *mtd; 22391809ed5SPiotr Ziecik flash_info_t *fi; 22491809ed5SPiotr Ziecik int error, i; 225419a1fe9SWolfgang Denk #ifdef CONFIG_MTD_CONCAT 2260a572655SStefan Roese int devices_found = 0; 2270a572655SStefan Roese struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS]; 228419a1fe9SWolfgang Denk #endif 22991809ed5SPiotr Ziecik 23091809ed5SPiotr Ziecik for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 23191809ed5SPiotr Ziecik fi = &flash_info[i]; 23291809ed5SPiotr Ziecik mtd = &cfi_mtd_info[i]; 23391809ed5SPiotr Ziecik 23491809ed5SPiotr Ziecik memset(mtd, 0, sizeof(struct mtd_info)); 23591809ed5SPiotr Ziecik 23691809ed5SPiotr Ziecik error = cfi_mtd_set_erasesize(mtd, fi); 23791809ed5SPiotr Ziecik if (error) 23891809ed5SPiotr Ziecik continue; 23991809ed5SPiotr Ziecik 240c203ef5dSAndreas Huber sprintf(cfi_mtd_names[i], "nor%d", i); 241c203ef5dSAndreas Huber mtd->name = cfi_mtd_names[i]; 24291809ed5SPiotr Ziecik mtd->type = MTD_NORFLASH; 24391809ed5SPiotr Ziecik mtd->flags = MTD_CAP_NORFLASH; 24491809ed5SPiotr Ziecik mtd->size = fi->size; 24591809ed5SPiotr Ziecik mtd->writesize = 1; 24691809ed5SPiotr Ziecik 247*dfe64e2cSSergey Lapin mtd->_erase = cfi_mtd_erase; 248*dfe64e2cSSergey Lapin mtd->_read = cfi_mtd_read; 249*dfe64e2cSSergey Lapin mtd->_write = cfi_mtd_write; 250*dfe64e2cSSergey Lapin mtd->_sync = cfi_mtd_sync; 251*dfe64e2cSSergey Lapin mtd->_lock = cfi_mtd_lock; 252*dfe64e2cSSergey Lapin mtd->_unlock = cfi_mtd_unlock; 25391809ed5SPiotr Ziecik mtd->priv = fi; 25491809ed5SPiotr Ziecik 25591809ed5SPiotr Ziecik if (add_mtd_device(mtd)) 25691809ed5SPiotr Ziecik return -ENOMEM; 2570a572655SStefan Roese 258419a1fe9SWolfgang Denk #ifdef CONFIG_MTD_CONCAT 2590a572655SStefan Roese mtd_list[devices_found++] = mtd; 260419a1fe9SWolfgang Denk #endif 26191809ed5SPiotr Ziecik } 26291809ed5SPiotr Ziecik 2630a572655SStefan Roese #ifdef CONFIG_MTD_CONCAT 2640a572655SStefan Roese if (devices_found > 1) { 2650a572655SStefan Roese /* 2660a572655SStefan Roese * We detected multiple devices. Concatenate them together. 2670a572655SStefan Roese */ 2680a572655SStefan Roese sprintf(c_mtd_name, "nor%d", devices_found); 2690a572655SStefan Roese mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name); 2700a572655SStefan Roese 2710a572655SStefan Roese if (mtd == NULL) 2720a572655SStefan Roese return -ENXIO; 2730a572655SStefan Roese 2740a572655SStefan Roese if (add_mtd_device(mtd)) 2750a572655SStefan Roese return -ENOMEM; 2760a572655SStefan Roese } 2770a572655SStefan Roese #endif /* CONFIG_MTD_CONCAT */ 2780a572655SStefan Roese 27991809ed5SPiotr Ziecik return 0; 28091809ed5SPiotr Ziecik } 281