/*++ drivers/mtd/devices/wmt_sf.c Copyright (c) 2008 WonderMedia Technologies, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. --*/ /* #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include */ //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wmt_sf.h" SF_FPTR wmt_sf_prot = 0; EXPORT_SYMBOL(wmt_sf_prot); typedef enum { FL_READY, FL_READING, FL_WRITING, FL_ERASING, } sf_state_t; struct sf_hw_control { spinlock_t lock; wait_queue_head_t wq; //struct sf_chip *active; }; struct wmt_sf_info_t { struct mtd_info *sfmtd; struct mtd_info mtd; struct mutex lock; struct sfreg_t *reg ; void *io_base; struct sf_hw_control controller; sf_state_t state; }; static struct mutex sector_lock; static struct sfreg_t *reg_sf; extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); extern int wmt_setsyspara(char *varname, char *varval); extern int wmt_is_secure_enabled(void); /* * 512k Flash * +------------------------+ * | | 0xffff,ffff * | 64k W-Load | * | | * +------------------------+ 0xffff,0000 * | | * | 64k OTP env | * | | * +------------------------+ 0xfffe,0000 * | | * | 64k uboot env | * | | * +------------------------+ 0xfffd,0000 * | | * | | * | 320k uboot.bin | * | | * | | * | | * ~~~~~~~~~~~~~~~~~~~~~~~~~~ 0xfff8,0000 */ /* only bootable devices have a default partitioning */ /*static*/ struct mtd_partition boot_partitions[] = { { .name = "u-boot-SF", .offset = 0x00000000, .size = 0x00050000, }, { .name = "u-boot env. cfg. 1-SF", .offset = 0x00050000, .size = 0x00010000, }, #if 0 // for OTP env partition { .name = "otp-env-SF", .offset = 0x00060000, .size = 0x00010000, }, #endif { .name = "w-load-SF", .offset = 0x00070000, .size = 0x00010000, } }; #define NUM_SF_PARTITIONS ARRAY_SIZE(boot_partitions) struct mtd_partition boot_partitions_16m[] = { { .name = "boot-img-SF", .offset = 0x00000000, .size = 0x00f40000, }, { .name = "logo-SF", .offset = 0x00f40000, .size = 0x00040000, }, { .name = "u-boot-SF", .offset = 0x00f80000, .size = 0x00050000, }, { .name = "u-boot env. cfg. 1-SF", .offset = 0x00fd0000, .size = 0x00010000, }, #if 0 // for OTP env partition { .name = "otp-env-SF", .offset = 0x00fe0000, .size = 0x00010000, }, #endif { .name = "w-load-SF", .offset = 0x00ff0000, .size = 0x00010000, } }; #define NUM_SF_PARTITIONS_16M ARRAY_SIZE(boot_partitions_16m) static const char *part_probes[] = { "cmdlinepart", NULL }; static struct mtd_partition *parts; static unsigned int g_sf_force_size; static int __init wmt_force_sf_size(char *str) { char dummy; sscanf(str, "%d%c", (int *)&g_sf_force_size, &dummy); return 1; } __setup("sf_mtd=", wmt_force_sf_size); struct wmt_flash_info_t g_sf_info[2]; extern struct wm_sf_dev_t sf_ids[]; unsigned int MTDSF_PHY_ADDR; int get_sf_info(int index, struct wmt_flash_info_t *info) { unsigned int i; if (info->id == FLASH_UNKNOW) return -1; for (i = 0; sf_ids[i].id != 0; i++) { if (sf_ids[i].id == info->id) { info->total = (sf_ids[i].size*1024); break; } } if (sf_ids[i].id == 0) { printk(KERN_WARNING "un-know id%d = 0x%x\n", index, info->id); if (index == 0 && info->id != 0) { // if not identified, set size 512K as default. info->id = sf_ids[2].id; info->total = (sf_ids[2].size*1024); } else { info->id = FLASH_UNKNOW; info->total = 0; } return -1; } return 0; } int wmt_sfc_ccr(struct wmt_flash_info_t *info) { unsigned int cnt = 0, size; size = info->total; while (size) { size >>= 1; cnt++; } cnt -= 16; cnt = cnt<<8; info->val = (info->phy|cnt); return 0; } int wmt_sfc_init(struct sfreg_t *sfc) { unsigned int tmp; int i, ret; tmp = STRAP_STATUS_VAL; if ((tmp & 0x4008) == (0x4000|SPI_FLASH_TYPE)) { MTDSF_PHY_ADDR = 0xFFFFFFFF; /* set default */ sfc->SPI_RD_WR_CTR = 0x11; /*sfc->CHIP_SEL_0_CFG = 0xFF800800;*/ sfc->SPI_INTF_CFG = 0x00030000; sfc->SPI_ERROR_STATUS = 0x3F; } else { /*MTDSF_PHY_ADDR = 0xEFFFFFFF;*/ /* set default */ /*sfc->SPI_RD_WR_CTR = 0x11; sfc->CHIP_SEL_0_CFG = 0xEF800800; sfc->SPI_INTF_CFG = 0x00030000;*/ printk(KERN_WARNING "strapping not support sf\n"); return -EIO; } memset(&g_sf_info[0], 0, 2*sizeof(struct wmt_flash_info_t)); g_sf_info[0].id = FLASH_UNKNOW; g_sf_info[1].id = FLASH_UNKNOW; /* read id */ sfc->SPI_RD_WR_CTR = 0x11; g_sf_info[0].id = sfc->SPI_MEM_0_SR_ACC; sfc->SPI_RD_WR_CTR = 0x01; sfc->SPI_RD_WR_CTR = 0x11; g_sf_info[1].id = sfc->SPI_MEM_1_SR_ACC; sfc->SPI_RD_WR_CTR = 0x01; printk("wmt_sfc_init id0 is %x, id1 is %x\n", g_sf_info[0].id, g_sf_info[1].id); for (i = 0; i < 2; i++) { ret = get_sf_info(i, &g_sf_info[i]); if (ret) break; } if (g_sf_info[0].id == FLASH_UNKNOW) return -1; g_sf_info[0].phy = (MTDSF_PHY_ADDR-g_sf_info[0].total+1); MTDSF_PHY_ADDR = MTDSF_PHY_ADDR-g_sf_info[0].total+1; if (g_sf_info[0].phy&0xFFFF) { printk(KERN_ERR "WMT SFC Err : start address must align to 64KByte\n"); return -1; } wmt_sfc_ccr(&g_sf_info[0]); sfc->CHIP_SEL_0_CFG = g_sf_info[0].val; if (g_sf_info[1].id != FLASH_UNKNOW) { g_sf_info[1].phy = (g_sf_info[0].phy-g_sf_info[1].total); MTDSF_PHY_ADDR = MTDSF_PHY_ADDR-g_sf_info[1].total; tmp = g_sf_info[1].phy; g_sf_info[1].phy &= ~(g_sf_info[1].total-1); if (g_sf_info[0].phy&0xFFFF) { printk(KERN_ERR "WMT SFC Err : start address must align to 64KByte\n"); printk(KERN_ERR "WMT SFC Err : CS1 could not be used\n"); g_sf_info[1].id = FLASH_UNKNOW; return 0; } wmt_sfc_ccr(&g_sf_info[1]); sfc->CHIP_SEL_1_CFG = g_sf_info[1].val; } /*printk("CS0 : 0x%x , CS1 : 0x%x\n",g_sf_info[0].val,g_sf_info[1].val);*/ if (g_sf_force_size) { tmp = (g_sf_force_size*1024*1024); MTDSF_PHY_ADDR = (0xFFFFFFFF-tmp)+1; } return 0; } int flash_error(unsigned long code) { /* check Timeout */ if (code & BIT_TIMEOUT) { printk(KERN_ERR "Serial Flash Timeout\n");/* For UBOOT */ return ERR_TIMOUT; } if (code & SF_BIT_WR_PROT_ERR) { printk(KERN_ERR "Serial Flash Write Protect Error\n"); /* For UBOOT */ return ERR_PROG_ERROR; } if (code & SF_BIT_MEM_REGION_ERR) { printk(KERN_ERR "Serial Flash Memory Region Error\n") ;/* For UBOOT */ return ERR_PROG_ERROR; } if (code & SF_BIT_PWR_DWN_ACC_ERR) { printk(KERN_ERR "Serial Flash Power Down Access Error\n") ;/* For UBOOT */ return ERR_PROG_ERROR; } if (code & SF_BIT_PCMD_OP_ERR) { printk(KERN_ERR "Serial Flash Program CMD OP Error\n") ;/* For UBOOT */ return ERR_PROG_ERROR; } if (code & SF_BIT_PCMD_ACC_ERR) { printk(KERN_ERR "Serial Flash Program CMD OP Access Error\n") ;/* For UBOOT */ return ERR_PROG_ERROR; } if (code & SF_BIT_MASLOCK_ERR) { printk(KERN_ERR "Serial Flash Master Lock Error\n") ;/* For UBOOT */ return ERR_PROG_ERROR; } /* OK, no error */ return ERR_OK; } int spi_read_status(int chip) { struct sfreg_t *sfreg = reg_sf; unsigned long temp, timeout = 0x30000000; int rc; auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); do { if (chip == 0) temp = sfreg->SPI_MEM_0_SR_ACC; else temp = sfreg->SPI_MEM_1_SR_ACC; /* please SPI flash data sheet */ if ((temp & 0x1) == 0x0) { //printk(KERN_ERR "ok re flash status=0x%x\n", (unsigned int)sfreg->SPI_MEM_0_SR_ACC); break; } rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { /*printk(KERN_ERR "re sts flash error rc = 0x%x\n", rc);*/ sfreg->SPI_ERROR_STATUS = 0x3F; /* write 1 to clear status*/ goto sf_err1; } else if (sfreg->SPI_ERROR_STATUS) { sfreg->SPI_ERROR_STATUS = 0x3F; printk(KERN_ERR "re flash error rc = 0x%x status=0x%x\n", rc, (unsigned int)sfreg->SPI_MEM_0_SR_ACC); } timeout--; } while (timeout); if (timeout == 0) { printk(KERN_ERR "Check SF status timeout\n"); return ERR_TIMOUT; } return 0; sf_err1: return rc; } EXPORT_SYMBOL(spi_read_status); int spi_write_status(int chip, unsigned int value) { struct sfreg_t *sfreg = reg_sf; int rc, index = 0, ii; unsigned int temp; ii = *(volatile unsigned char *)(GPIO_BASE_ADDR + 0xDF); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); rc = spi_read_status(chip); wr_sts: if (chip == 0) { sfreg->SPI_WR_EN_CTR = SF_CS0_WR_EN; sfreg->SPI_MEM_0_SR_ACC = value; } else { sfreg->SPI_WR_EN_CTR = SF_CS1_WR_EN; sfreg->SPI_MEM_1_SR_ACC = value; } rc = spi_read_status(chip); temp = sfreg->SPI_MEM_0_SR_ACC; if ((temp&1) == 0 && (value&0x9C) != (temp&0x9C)) { printk(KERN_ERR "0x%x wr sf sts reg 0x%x fail i=%d gpio=0x%x\n",value, temp, index, ii); if (index < 10) { index++; goto wr_sts; } else printk(KERN_ERR "write sf status reg 0x%x fail\n", temp); } sfreg->SPI_WR_EN_CTR = SF_CS0_WR_DIS; rc = spi_read_status(chip); auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); return rc; } EXPORT_SYMBOL_GPL(spi_write_status); int spi_flash_sector_erase(unsigned long addr, struct sfreg_t *sfreg) { unsigned long timeout = 0x600000; unsigned long temp ; int rc; mutex_lock(§or_lock); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); /* SPI module chip erase SPI flash write enable control register: write enable on chip sel 0 */ if ((addr + MTDSF_PHY_ADDR) >= g_sf_info[0].phy) { sfreg->SPI_WR_EN_CTR = SF_CS0_WR_EN ; /* printk("sfreg->SPI_ER_START_ADDR = %x \n",sfreg->SPI_ER_START_ADDR);*/ /*printk("!!!! Erase chip 0\n"); */ /* select sector to erase */ addr &= 0xFFFF0000; sfreg->SPI_ER_START_ADDR = (addr+MTDSF_PHY_ADDR); /* SPI flash erase control register: start chip erase Auto clear when transmit finishes. */ sfreg->SPI_ER_CTR = SF_SEC_ER_EN; /*printk("sfreg->SPI_ER_START_ADDR = %x \n",sfreg->SPI_ER_START_ADDR);*/ /* poll status reg of chip 0 for chip erase */ do { //printk("0s"); msleep(50); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); //printk(" 0e\n"); udelay(1); temp = sfreg->SPI_MEM_0_SR_ACC; /* please SPI flash data sheet */ if ((temp & 0x1) == 0x0) break; timeout--; } while (timeout); if (timeout == 0) goto er_err_timout; rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { /*printk(KERN_ERR "flash error rc = 0x%x\n", rc);*/ sfreg->SPI_ERROR_STATUS = 0x3F; /* write 1 to clear status*/ goto sf_err; } else if (sfreg->SPI_ERROR_STATUS) { printk(KERN_ERR "flash error rc = 0x%x status=0x%x\n", rc, (unsigned int)sfreg->SPI_MEM_0_SR_ACC); sfreg->SPI_ERROR_STATUS = 0x3F; printk(KERN_ERR "1flash error rc = 0x%x status=0x%x\n", rc, (unsigned int)sfreg->SPI_MEM_0_SR_ACC); } sfreg->SPI_WR_EN_CTR = SF_CS0_WR_DIS; goto sf_OK; } else { sfreg->SPI_WR_EN_CTR = SF_CS1_WR_EN; /* select sector to erase */ addr &= 0xFFFF0000; sfreg->SPI_ER_START_ADDR = (addr+MTDSF_PHY_ADDR); /* SPI flash erase control register: start chip erase Auto clear when transmit finishes. */ sfreg->SPI_ER_CTR = SF_SEC_ER_EN; /* poll status reg of chip 0 for chip erase */ do { //printk("1s"); msleep(50); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); //printk(" 1e\n"); udelay(1); temp = sfreg->SPI_MEM_1_SR_ACC; /* please SPI flash data sheet */ if ((temp & 0x1) == 0x0) break; rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { sfreg->SPI_ERROR_STATUS = 0x3F ; /* write 1 to clear status*/ goto sf_err; } timeout--; } while (timeout); if (timeout == 0) goto er_err_timout; sfreg->SPI_WR_EN_CTR = SF_CS1_WR_DIS ; goto sf_OK; } sf_OK: mutex_unlock(§or_lock); return ERR_OK; sf_err: mutex_unlock(§or_lock); return rc; er_err_timout: mutex_unlock(§or_lock); return ERR_TIMOUT; } /* We could store these in the mtd structure, but we only support 1 device.. static struct mtd_info *mtd_info; */ static int sf_erase(struct mtd_info *mtd, struct erase_info *instr) { int ret; struct wmt_sf_info_t *info = (struct wmt_sf_info_t *)mtd->priv; struct sfreg_t *sfreg = info->reg; mutex_lock(&info->lock); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); ret = spi_flash_sector_erase((unsigned long)instr->addr, sfreg); auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); mutex_unlock(&info->lock); if (ret != ERR_OK) { printk(KERN_ERR "sf_erase() error at address 0x%lx \n", (unsigned long)instr->addr); return -EINVAL; } instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0; } int sf_copy_env(char *dest, char *src, int len) { int i = 0; int rc,blk; char *s,*p; mutex_lock(§or_lock); rc = spi_read_status(0); if (rc){ printk("sfread: sf0 is busy"); } s = src; p = dest; blk = len/1024; if(len%1024) blk++; auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); memcpy(p, s, 0x400);//1K while((p[0x3fe]|p[0x3ff]) != '\0' && i++ < (blk -1)){ s += 0x400; p += 0x400; memcpy(p, s, 0x400);//1K } auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); mutex_unlock(§or_lock); return len; } static int sf_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct wmt_sf_info_t *info = (struct wmt_sf_info_t *)mtd->priv; unsigned char *sf_base_addr = info->io_base; int rc; mutex_lock(&info->lock); mutex_lock(§or_lock); //printk("re sf check"); if ((from + MTDSF_PHY_ADDR) >= g_sf_info[0].phy) { rc = spi_read_status(0); if (rc) printk("sfread: sf0 is busy"); } else { rc = spi_read_status(1); if (rc) printk("sfread: sf1 is busy"); } //printk("end\n"); /*printk("sf_read(pos:%x, len:%x)\n", (long)from, (long)len);*/ if (from + len > mtd->size) { printk(KERN_ERR "sf_read() out of bounds (%lx > %lx)\n", (long)(from + len), (long)mtd->size); return -EINVAL; } //printk("sfread: lock from%llx, len=%d\n", from, len); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); //_memcpy_fromio(buf, (sf_base_addr+from), len); memcpy(buf, (sf_base_addr+from), len); auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); mutex_unlock(§or_lock); mutex_unlock(&info->lock); *retlen = len; return 0; } int spi_flash_sector_write(struct sfreg_t *sfreg, unsigned char *sf_base_addr, loff_t to, size_t len, u_char *buf) { unsigned long temp; unsigned int i = 0; int rc ; unsigned long timeout = 0x30000000; size_t retlen; mutex_lock(§or_lock); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); udelay(1); //printk("wr sf check"); if ((to + MTDSF_PHY_ADDR) >= g_sf_info[0].phy) { rc = spi_read_status(0); if (rc) printk("wr c0 wait status ret=%d\n", rc); } else { rc = spi_read_status(1); if (rc) printk("wr c1 wait status ret=%d\n", rc); } //printk("end\n"); sfreg->SPI_WR_EN_CTR = 0x03; while (len >= 8) { _memcpy_toio(((u_char *)(sf_base_addr+to+i)), buf+i, 4); i += 4; _memcpy_toio(((u_char *)(sf_base_addr+to+i)), (buf+i), 4); i += 4; len -= 8; timeout = 0x30000000; do { temp = sfreg->SPI_MEM_0_SR_ACC ; /* please see SPI flash data sheet */ if ((temp & 0x1) == 0x0) break ; rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { sfreg->SPI_ERROR_STATUS = 0x3F ; /* write 1 to clear status */ goto sf_wr_err; } timeout--; } while (timeout); if (timeout == 0) { printk(KERN_ERR "time out \n"); goto err_timeout; } } while (len >= 4) { _memcpy_toio(((u_char *)(sf_base_addr+to+i)), (u_char*)(buf+i), 4); i += 4; len -= 4; if (len) { _memcpy_toio(((u_char *)(sf_base_addr+to+i)), (u_char*)(buf+i), 1); i++; len--; } timeout = 0x30000000; do { temp = sfreg->SPI_MEM_0_SR_ACC ; /* please see SPI flash data sheet */ if ((temp & 0x1) == 0x0) break; rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { sfreg->SPI_ERROR_STATUS = 0x3F ; /* write 1 to clear status */ goto sf_wr_err; } timeout--; } while (timeout); if (timeout == 0) { printk(KERN_ERR "time out \n"); goto err_timeout; } } while (len) { _memcpy_toio(((u_char *)(sf_base_addr+to+i)), (buf+i), 1); i++; len--; if (len) { _memcpy_toio(((u_char *)(sf_base_addr+to+i)), (buf+i), 1); i++; len--; } timeout = 0x30000000; do { temp = sfreg->SPI_MEM_0_SR_ACC ; /* please see SPI flash data sheet */ if ((temp & 0x1) == 0x0) break; rc = flash_error(sfreg->SPI_ERROR_STATUS); if (rc != ERR_OK) { sfreg->SPI_ERROR_STATUS = 0x3F ; /* write 1 to clear status */ goto sf_wr_err; } timeout--; } while (timeout); if (timeout == 0) { printk(KERN_ERR "time out \n"); goto err_timeout; } } retlen = i; sfreg->SPI_WR_EN_CTR = 0x00; //REG32_VAL(PMCEU_ADDR) &= ~(SF_CLOCK_EN); mutex_unlock(§or_lock); return retlen; err_timeout: mutex_unlock(§or_lock); return ERR_TIMOUT; sf_wr_err: mutex_unlock(§or_lock); return rc; } static int sf_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct wmt_sf_info_t *info = (struct wmt_sf_info_t *)mtd->priv; unsigned char *sf_base_addr = info->io_base; struct sfreg_t *sfreg = info->reg; size_t ret; /*printk("sf_write(pos:0x%x, len:0x%x )\n", (long)to, (long)len);*/ if (to + len > mtd->size) { printk(KERN_ERR "sf_write() out of bounds (%ld > %ld)\n", (long)(to + len), (long)mtd->size); return -EINVAL; } mutex_lock(&info->lock); auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); ret = spi_flash_sector_write(sfreg, sf_base_addr, to, len, (u_char *)buf); auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); mutex_unlock(&info->lock); *retlen = ret; return 0; } #if 0 void print_reg() { printk(KERN_INFO "sfreg->CHIP_SEL_0_CFG = %lx\n", sfreg->CHIP_SEL_0_CFG); printk(KERN_INFO "sfreg->CHIP_SEL_1_CFG = %lx\n", sfreg->CHIP_SEL_1_CFG); printk(KERN_INFO "sfreg->SPI_WR_EN_CTR = %lx \n", sfreg->SPI_WR_EN_CTR); printk(KERN_INFO "sfreg->SPI_ER_CTR = %lx \n", sfreg->SPI_ER_CTR); printk(KERN_INFO "sfreg->SPI_ER_START_ADDR = %lx \n", sfreg->SPI_ER_START_ADDR); } void identify_sf_device_id(int sf_num) { sfreg->SPI_RD_WR_CTR = 0x10; if (sf_num == 0) printk(KERN_INFO "sfreg->SPI_MEM_0_SR_ACC=%lx\n", sfreg->SPI_MEM_0_SR_ACC); else if (sf_num == 1) printk(KERN_INFO "sfreg->SPI_MEM_0_SR_ACC=%lx\n", sfreg->SPI_MEM_0_SR_ACC); else printk(KERN_ERR "Unkown spi flash! \n"); } #endif void config_sf_reg(struct sfreg_t *sfreg) { #if 0 sfreg->CHIP_SEL_0_CFG = (MTDSF_PHY_ADDR | 0x0800800); /*0xff800800;*/ sfreg->CHIP_SEL_1_CFG = (MTDSF_PHY_ADDR | 0x0800); /*0xff000800;*/ sfreg->SPI_INTF_CFG = 0x00030000; printk(KERN_INFO "Eric %s Enter chip0=%x chip1=%x\n" , __func__, sfreg->CHIP_SEL_0_CFG, sfreg->CHIP_SEL_1_CFG); #else if (g_sf_info[0].val) sfreg->CHIP_SEL_0_CFG = g_sf_info[0].val; if (g_sf_info[1].val) sfreg->CHIP_SEL_1_CFG = g_sf_info[1].val; else sfreg->CHIP_SEL_1_CFG = 0xff780800; sfreg->SPI_INTF_CFG = 0x00030000; #endif } /* void shift_partition_content(int index) { int i, j; for ( i = index, j = 0; i < 6; i++, j++) { boot_partitions[j].name = boot_partitions[i].name; boot_partitions[j].offset = boot_partitions[i].offset; boot_partitions[j].size = boot_partitions[i].size; } } */ static int sf_erase_disabled(struct mtd_info *mtd, struct erase_info *instr) { printk(KERN_WARNING "sf_erase addr 0x%llx, len 0x%llx denied\n", instr->addr, instr->len); return -EPERM; } static int sf_write_disabled(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { printk(KERN_WARNING "sf_write addr 0x%llx, len 0x%x denied\n", to, len); return -EPERM; } int mtdsf_init_device(struct mtd_info *mtd, unsigned long size, char *name) { int i; int nr_parts = 0, cut_parts = 0, ret; int secure = wmt_is_secure_enabled(); mtd->name = name; mtd->type = MTD_NORFLASH; mtd->flags = MTD_CAP_NORFLASH; mtd->size = size; mtd->erasesize = MTDSF_ERASE_SIZE; mtd->owner = THIS_MODULE; mtd->_read = sf_read; if(secure){ mtd->_erase = sf_erase_disabled; mtd->_write = sf_write_disabled; } else { mtd->_erase = sf_erase; mtd->_write = sf_write; } mtd->writesize = 1; if(size == 0x1000000){ parts = boot_partitions_16m; nr_parts = NUM_SF_PARTITIONS_16M; }else{ parts = boot_partitions; nr_parts = NUM_SF_PARTITIONS; for(i=0; i < NUM_SF_PARTITIONS; i++){ parts[i].offset += (0XFFF80000-MTDSF_PHY_ADDR); } } printk(KERN_INFO "SF Using builtin partition table count=%d %s\n", nr_parts - cut_parts, secure ? "secure" : "" ); ret = mtd_device_parse_register(mtd, part_probes, NULL, parts, nr_parts - cut_parts); /*if (ret) { dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret); }*/ return ret; } static int wmt_sf_probe(struct platform_device *pdev) { int err; /*int retval, len = 40; char *buf[40]; char *buf1 = "7533967";*/ /* struct platform_device *pdev = to_platform_device(dev);*/ struct wmt_sf_info_t *info; unsigned int sfsize = 0; auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); //REG32_VAL(0xFE130314) = 0xc; printk("sf clock =0x%x \n", REG32_VAL(0xFE130314)); info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; mutex_init(&info->lock); mutex_init(§or_lock); info->sfmtd = &info->mtd; dev_set_drvdata(&pdev->dev, info); info->reg = (struct sfreg_t *)SF_BASE_ADDR; /*config_sf_reg(info->reg);*/ if (info->reg) err = wmt_sfc_init(info->reg); else err = -EIO; if (err) { printk(KERN_ERR "wmt sf controller initial failed\n"); goto exit_error; } if (g_sf_force_size) sfsize = (g_sf_force_size*1024*1024); else sfsize = (0xFFFFFFFF-MTDSF_PHY_ADDR)+1;//MTDSF_TOTAL_SIZE; printk("MTDSF_PHY_ADDR = %08X, sfsize = %08X\n",MTDSF_PHY_ADDR,sfsize); if (MTDSF_PHY_ADDR == 0xFFFFFFFF || MTDSF_PHY_ADDR == 0xEFFFFFFF) { MTDSF_PHY_ADDR = MTDSF_PHY_ADDR-sfsize+1; printk("MTDSF_PHY_ADDR = %08X, sfsize = %08X\n",MTDSF_PHY_ADDR,sfsize); } info->io_base = (unsigned char *)ioremap(MTDSF_PHY_ADDR, sfsize); if (info->io_base == NULL) { dev_err(&pdev->dev, "cannot reserve register region\n"); err = -EIO; goto exit_error; } err = mtdsf_init_device(info->sfmtd, sfsize, "mtdsf device"); if (err) goto exit_error; info->sfmtd->priv = info; reg_sf = info->reg; //for global use. /* retval = wmt_getsyspara("dan", buf, &len); printk(KERN_INFO "sf read env buf=%s\n", buf); retval = wmt_setsyspara("dan", buf1); retval = wmt_getsyspara("dan", buf, &len); printk(KERN_INFO "sf read env buf=%s\n", buf);*/ auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); printk(KERN_INFO "wmt sf controller initial ok\n"); exit_error: return err; } /*static int wmt_sf_remove(struct device *dev)*/ static int wmt_sf_remove(struct platform_device *pdev) { struct wmt_sf_info_t *info = dev_get_drvdata(&pdev->dev); int status; pr_debug("%s: remove\n", dev_name(&pdev->dev)); status = mtd_device_unregister(&info->mtd); if (status == 0) { dev_set_drvdata(&pdev->dev, NULL); if (info->io_base) iounmap(info->io_base); kfree(info); } return 0; } #ifdef CONFIG_PM int wmt_sf_suspend(struct platform_device *pdev, pm_message_t state) { unsigned int boot_value = STRAP_STATUS_VAL; int rc = 0; /*Judge whether boot from SF in order to implement power self management*/ if ((boot_value & 0x4008) == (0x4000|SPI_FLASH_TYPE)) { auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); rc = spi_read_status(0); if (rc) printk("sfread: sf0 is busy"); } printk("suspend pllc=0x%x, div0x%x\n", *(volatile unsigned int *)(0xfe130208), *(volatile unsigned int *)(0xfe13036c)); printk(KERN_INFO "wmt_sf_suspend\n"); return 0; } int wmt_sf_resume(struct platform_device *pdev) { struct wmt_sf_info_t *info = dev_get_drvdata(&pdev->dev); struct sfreg_t *sfreg = info->reg; auto_pll_divisor(DEV_SF, CLK_ENABLE, 0, 0); if (info->reg) config_sf_reg(info->reg); else printk(KERN_ERR "wmt sf restore state error\n"); if (g_sf_info[0].id == SF_IDALL(ATMEL_MANUF, AT_25DF041A_ID)) { printk(KERN_INFO "sf resume and set Global Unprotect\n"); sfreg->SPI_INTF_CFG |= SF_MANUAL_MODE; /* enter programmable command mode */ sfreg->SPI_PROG_CMD_WBF[0] = SF_CMD_WREN; sfreg->SPI_PROG_CMD_CTR = (0x01000000 | (0<<1)); /* set size and chip select */ sfreg->SPI_PROG_CMD_CTR |= SF_RUN_CMD; /* enable programmable command */ while ((sfreg->SPI_PROG_CMD_CTR & SF_RUN_CMD) != 0) ; sfreg->SPI_PROG_CMD_WBF[0] = SF_CMD_WRSR; sfreg->SPI_PROG_CMD_WBF[1] = 0x00; /* Global Unprotect */ sfreg->SPI_PROG_CMD_CTR = (0x02000000 | (0<<1)); /* set size and chip select */ sfreg->SPI_PROG_CMD_CTR |= SF_RUN_CMD; /* enable programmable command */ while ((sfreg->SPI_PROG_CMD_CTR & SF_RUN_CMD) != 0) ; sfreg->SPI_PROG_CMD_CTR = 0; /* reset programmable command register*/ sfreg->SPI_INTF_CFG &= ~SF_MANUAL_MODE; /* leave programmable mode */ } auto_pll_divisor(DEV_SF, CLK_DISABLE, 0, 0); printk("resume pllc=0x%x, div0x%x\n", *(volatile unsigned int *)(0xfe130208), *(volatile unsigned int *)(0xfe13036c)); return 0; } #else #define wmt_sf_suspend NULL #define wmt_sf_resume NULL #endif /* struct device_driver wmt_sf_driver = { .name = "sf", .bus = &platform_bus_type, .probe = wmt_sf_probe, .remove = wmt_sf_remove, .suspend = wmt_sf_suspend, .resume = wmt_sf_resume }; */ struct platform_driver wmt_sf_driver = { .driver.name = "sf", .probe = wmt_sf_probe, .remove = wmt_sf_remove, .suspend = wmt_sf_suspend, .resume = wmt_sf_resume }; static int __init wmt_sf_init(void) { //printk(KERN_INFO "WMT SPI Flash Driver, WonderMedia Technologies, Inc\n"); return platform_driver_register(&wmt_sf_driver); } static void __exit wmt_sf_exit(void) { platform_driver_unregister(&wmt_sf_driver); } module_init(wmt_sf_init); module_exit(wmt_sf_exit); MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_DESCRIPTION("WMT [SF] driver"); MODULE_LICENSE("GPL");