summaryrefslogtreecommitdiff
path: root/drivers/mtd/devices/wmt_sf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/devices/wmt_sf.c')
-rwxr-xr-xdrivers/mtd/devices/wmt_sf.c1112
1 files changed, 1112 insertions, 0 deletions
diff --git a/drivers/mtd/devices/wmt_sf.c b/drivers/mtd/devices/wmt_sf.c
new file mode 100755
index 00000000..d0463425
--- /dev/null
+++ b/drivers/mtd/devices/wmt_sf.c
@@ -0,0 +1,1112 @@
+/*++
+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 <http://www.gnu.org/licenses/>.
+
+WonderMedia Technologies, Inc.
+10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+--*/
+
+/*
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/sizes.h>
+
+#include <asm/arch/vt8610_pmc.h>
+#include <asm/arch/vt8610_gpio.h>
+#include <asm/arch/vt8610_dma.h>
+*/
+
+//#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/mtd/mtd.h>
+#include <asm/io.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <linux/delay.h>
+
+#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(&sector_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(&sector_lock);
+ return ERR_OK;
+sf_err:
+ mutex_unlock(&sector_lock);
+ return rc;
+er_err_timout:
+ mutex_unlock(&sector_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(&sector_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(&sector_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(&sector_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(&sector_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(&sector_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(&sector_lock);
+ return retlen;
+
+err_timeout:
+ mutex_unlock(&sector_lock);
+ return ERR_TIMOUT;
+sf_wr_err:
+ mutex_unlock(&sector_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(&sector_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");