diff --git a/drivers/block/sunxi_nand/nfc/nfc_w.c b/drivers/block/sunxi_nand/nfc/nfc_w.c index c9e5b78..4492c11 100644 --- a/drivers/block/sunxi_nand/nfc/nfc_w.c +++ b/drivers/block/sunxi_nand/nfc/nfc_w.c @@ -53,6 +53,9 @@ extern __s32 _vender_get_param(__u8 *para, __u8 *addr, __u32 count); extern __s32 _vender_set_param(__u8 *para, __u8 *addr, __u32 count); extern __s32 _vender_pre_condition(void); +__u8 nfc_1k_page_size = 1; +__u8 nfc_1k_ecc_mode = 8; + /*after send write or erase command, must wait rb from ready to busy, then can send status command because nfc not do this, so software delay by xr, 2009-3-25*/ @@ -318,8 +321,8 @@ __s32 NFC_Write_1K( NFC_CMD_LIST *wcmd, void *mainbuf, void *sparebuf, __u8 dm NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL)) | NFC_RAM_METHOD); //set pagesize to 1K - page_size_temp = (NFC_READ_REG(NFC_REG_CTL) & 0xf00)>>8; - NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL)) | (0x3<<8)); + page_size_temp = (NFC_READ_REG(NFC_REG_CTL) & 0xf00)>>8; + NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL) & ~0xf00) | (0x3<<8)); // /*set dma and run*/ // if (NFC_IS_SDRAM((__u32)mainbuf)) @@ -327,19 +330,19 @@ __s32 NFC_Write_1K( NFC_CMD_LIST *wcmd, void *mainbuf, void *sparebuf, __u8 dm // else // attr = 0x2930280; - this_dma_handle = dma_map_single(NULL, mainbuf, 1024, + this_dma_handle = dma_map_single(NULL, mainbuf, 1024 * nfc_1k_page_size, DMA_TO_DEVICE); - NAND_Config_Start_DMA(1, this_dma_handle, 1024); + NAND_Config_Start_DMA(1, this_dma_handle, 1024 * nfc_1k_page_size); /*wait cmd fifo free*/ ret = _wait_cmdfifo_free(); if (ret){ _exit_nand_critical(); - dma_unmap_single(NULL, this_dma_handle, 1024, DMA_TO_DEVICE); + dma_unmap_single(NULL, this_dma_handle, 1024 * nfc_1k_page_size, DMA_TO_DEVICE); return ret; } /*set NFC_REG_CNT*/ - NFC_WRITE_REG(NFC_REG_CNT,1024); + NFC_WRITE_REG(NFC_REG_CNT, 1024); /*set NFC_REG_RCMD_SET*/ cfg = 0; @@ -348,12 +351,11 @@ __s32 NFC_Write_1K( NFC_CMD_LIST *wcmd, void *mainbuf, void *sparebuf, __u8 dm NFC_WRITE_REG(NFC_REG_WCMD_SET, cfg); /*set NFC_REG_SECTOR_NUM*/ - NFC_WRITE_REG(NFC_REG_SECTOR_NUM, 1024/1024); + NFC_WRITE_REG(NFC_REG_SECTOR_NUM, nfc_1k_page_size); /*set user data*/ - for (i = 0; i < 1024/1024; i++){ + for (i = 0; i < nfc_1k_page_size; i++) NFC_WRITE_REG(NFC_REG_USER_DATA(i), *((__u32 *)sparebuf + i) ); - } /*set addr*/ _set_addr(program_addr_cmd->addr,program_addr_cmd->addr_cycle); @@ -361,29 +363,26 @@ __s32 NFC_Write_1K( NFC_CMD_LIST *wcmd, void *mainbuf, void *sparebuf, __u8 dm /*set NFC_REG_CMD*/ cfg = 0; /*set sequence mode*/ - cfg |= 0x1<<25; cfg |= program_addr_cmd->value; cfg |= ( (program_addr_cmd->addr_cycle - 1) << 16); - //cfg |= (NFC_SEND_ADR | NFC_ACCESS_DIR | NFC_DATA_TRANS | NFC_SEND_CMD | NFC_WAIT_FLAG | NFC_DATA_SWAP_METHOD); - cfg |= (NFC_SEND_ADR | NFC_ACCESS_DIR | NFC_DATA_TRANS | NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_SWAP_METHOD); + cfg |= (NFC_SEND_ADR | NFC_ACCESS_DIR | NFC_DATA_TRANS | NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_SWAP_METHOD | NFC_SEQ); cfg |= ((__u32)0x2 << 30);//page command - if (pagesize/1024 == 1) - cfg |= NFC_SEQ; /*enable ecc*/ _enable_ecc(1); /*set ecc to 64-bit ecc*/ - ecc_mode_temp = NFC_READ_REG(NFC_REG_ECC_CTL) & 0xf000; - NFC_WRITE_REG(NFC_REG_ECC_CTL, ((NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE))|(0x8<<12) )); + ecc_mode_temp = NFC_READ_REG(NFC_REG_ECC_CTL) & 0xf000; + NFC_WRITE_REG(NFC_REG_ECC_CTL, ((NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE)) | (nfc_1k_ecc_mode << 12))); + NFC_WRITE_REG(NFC_REG_CMD,cfg); - NAND_WaitDmaFinish(); + NAND_WaitDmaFinish(); _wait_twb(); _wait_cmdfifo_free(); _wait_cmd_finish(); - dma_unmap_single(NULL, this_dma_handle, 1024, DMA_TO_DEVICE); + dma_unmap_single(NULL, this_dma_handle, 1024 * nfc_1k_page_size, DMA_TO_DEVICE); /*disable ecc*/ _disable_ecc(); @@ -391,8 +390,8 @@ __s32 NFC_Write_1K( NFC_CMD_LIST *wcmd, void *mainbuf, void *sparebuf, __u8 dm /*set ecc to original value*/ NFC_WRITE_REG(NFC_REG_ECC_CTL, (NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE))|ecc_mode_temp); - /*set pagesize to original value*/ - NFC_WRITE_REG(NFC_REG_CTL, ((NFC_READ_REG(NFC_REG_CTL)) & (~NFC_PAGE_SIZE)) | (page_size_temp<<8)); + /*set pagesize to original value*/ + NFC_WRITE_REG(NFC_REG_CTL, ((NFC_READ_REG(NFC_REG_CTL)) & (~NFC_PAGE_SIZE)) | (page_size_temp<<8)); /*switch to ahb*/ NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL)) & (~NFC_RAM_METHOD)); @@ -622,8 +621,8 @@ __s32 _read_in_page_mode_1K(NFC_CMD_LIST *rcmd,void *mainbuf,void *sparebuf,__u NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL)) | NFC_RAM_METHOD); //set pagesize to 1K - page_size_temp = (NFC_READ_REG(NFC_REG_CTL) & 0xf00)>>8; - NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL)) | (0x3<<8)); + page_size_temp = (NFC_READ_REG(NFC_REG_CTL) & 0xf00)>>8; + NFC_WRITE_REG(NFC_REG_CTL, (NFC_READ_REG(NFC_REG_CTL) & ~0xf00) | (0x3<<8)); ///*set dma and run*/ ///*sdram*/ @@ -632,19 +631,19 @@ __s32 _read_in_page_mode_1K(NFC_CMD_LIST *rcmd,void *mainbuf,void *sparebuf,__u ///*sram*/ //else // attr = 0x2800293; - this_dma_handle = dma_map_single(NULL, mainbuf, 1024, + this_dma_handle = dma_map_single(NULL, mainbuf, 1024 * nfc_1k_page_size, DMA_FROM_DEVICE); - NAND_Config_Start_DMA(0, this_dma_handle, 1024); + NAND_Config_Start_DMA(0, this_dma_handle, 1024 * nfc_1k_page_size); /*wait cmd fifo free*/ ret = _wait_cmdfifo_free(); if (ret) { - dma_unmap_single(NULL, this_dma_handle, 1024, DMA_FROM_DEVICE); + dma_unmap_single(NULL, this_dma_handle, 1024 * nfc_1k_page_size, DMA_FROM_DEVICE); return ret; } /*set NFC_REG_CNT*/ - NFC_WRITE_REG(NFC_REG_CNT,1024); + NFC_WRITE_REG(NFC_REG_CNT, 1024 * nfc_1k_page_size); /*set NFC_REG_RCMD_SET*/ cfg = 0; @@ -654,7 +653,7 @@ __s32 _read_in_page_mode_1K(NFC_CMD_LIST *rcmd,void *mainbuf,void *sparebuf,__u NFC_WRITE_REG(NFC_REG_RCMD_SET, cfg); /*set NFC_REG_SECTOR_NUM*/ - NFC_WRITE_REG(NFC_REG_SECTOR_NUM, 1024/1024); + NFC_WRITE_REG(NFC_REG_SECTOR_NUM, nfc_1k_page_size); /*set addr*/ _set_addr(read_addr_cmd->addr,read_addr_cmd->addr_cycle); @@ -662,55 +661,49 @@ __s32 _read_in_page_mode_1K(NFC_CMD_LIST *rcmd,void *mainbuf,void *sparebuf,__u /*set NFC_REG_CMD*/ cfg = 0; cfg |= read_addr_cmd->value; - /*set sequence mode*/ - cfg |= 0x1<<25; cfg |= ( (read_addr_cmd->addr_cycle - 1) << 16); - cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_WAIT_FLAG | NFC_DATA_SWAP_METHOD); + cfg |= (NFC_SEND_ADR | NFC_DATA_TRANS | NFC_SEQ | NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_WAIT_FLAG | NFC_DATA_SWAP_METHOD); cfg |= ((__u32)0x2 << 30);//page command - if (1024/1024 == 1) - cfg |= NFC_SEQ; - /*enable ecc*/ _enable_ecc(1); /*set ecc to 64-bit ecc*/ - ecc_mode_temp = NFC_READ_REG(NFC_REG_ECC_CTL) & 0xf000; - NFC_WRITE_REG(NFC_REG_ECC_CTL, ((NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE))|(0x8<<12) )); + ecc_mode_temp = NFC_READ_REG(NFC_REG_ECC_CTL) & 0xf000; + NFC_WRITE_REG(NFC_REG_ECC_CTL, ((NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE))|(nfc_1k_ecc_mode<<12) )); NFC_WRITE_REG(NFC_REG_CMD,cfg); - NAND_WaitDmaFinish(); + NAND_WaitDmaFinish(); /*wait cmd fifo free and cmd finish*/ ret = _wait_cmdfifo_free(); ret |= _wait_cmd_finish(); - dma_unmap_single(NULL, this_dma_handle, 1024, DMA_FROM_DEVICE); + dma_unmap_single(NULL, this_dma_handle, 1024 * nfc_1k_page_size, DMA_FROM_DEVICE); if (ret){ _disable_ecc(); return ret; } /*get user data*/ - for (i = 0; i < 1024/1024; i++){ + for (i = 0; i < nfc_1k_page_size; i++){ *(((__u32*) sparebuf)+i) = NFC_READ_REG(NFC_REG_USER_DATA(i)); } /*ecc check and disable ecc*/ - ret = _check_ecc(pagesize/1024); + ret = _check_ecc(nfc_1k_page_size); _disable_ecc(); /*set ecc to original value*/ NFC_WRITE_REG(NFC_REG_ECC_CTL, (NFC_READ_REG(NFC_REG_ECC_CTL) & (~NFC_ECC_MODE))|ecc_mode_temp); - /*set pagesize to original value*/ - NFC_WRITE_REG(NFC_REG_CTL, ((NFC_READ_REG(NFC_REG_CTL)) & (~NFC_PAGE_SIZE)) | (page_size_temp<<8)); + /*set pagesize to original value*/ + NFC_WRITE_REG(NFC_REG_CTL, ((NFC_READ_REG(NFC_REG_CTL)) & (~NFC_PAGE_SIZE)) | (page_size_temp<<8)); return ret; } - __s32 _read_in_page_mode_spare(NFC_CMD_LIST *rcmd,void *mainbuf,void *sparebuf,__u8 dma_wait_mode) { __s32 ret; diff --git a/drivers/block/sunxi_nand/nfd/nand_blk.c b/drivers/block/sunxi_nand/nfd/nand_blk.c index a632453..f20cb8c 100644 --- a/drivers/block/sunxi_nand/nfd/nand_blk.c +++ b/drivers/block/sunxi_nand/nfd/nand_blk.c @@ -4,6 +4,8 @@ * (C) Copyright 2007-2012 * Allwinner Technology Co., Ltd. * + * /dev/rawnand support - Stan Skowronek + * * 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 @@ -35,13 +37,16 @@ #include #include #include +#include #include #include #include #include +#include #include "../src/include/nand_type.h" #include "../src/include/nand_drv_cfg.h" +#include "../src/include/nand_simple.h" #include "../nfc/nfc_i.h" #include "nand_blk.h" #include @@ -1043,12 +1048,12 @@ void set_nand_pio(void) cfg2 = *(volatile __u32 *)(gpio_base + 0x50); /*set PIOC for nand*/ - cfg0 &= 0x0; + cfg0 &= 0x00000000; cfg0 |= 0x22222222; - cfg1 &= 0x0; + cfg1 &= 0x00000000; cfg1 |= 0x22222222; - cfg2 &= 0x0; - cfg2 |= 0x22222222; + cfg2 &= 0x0000000f; + cfg2 |= 0x22222220; *(volatile __u32 *)(gpio_base + 0x48) = cfg0; *(volatile __u32 *)(gpio_base + 0x4c) = cfg1; @@ -1234,6 +1239,226 @@ __u32 nand_get_module_clk(void) #endif #ifndef CONFIG_SUNXI_NAND_TEST + +static char *rawnand_xferbuf = NULL; + +static loff_t rawnand_llseek(struct file *file, loff_t off, int whence) +{ + loff_t newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + case 1: /* SEEK_CUR */ + newpos = file->f_pos + off; + break; + default: /* can't happen */ + return -EINVAL; + } + + if(newpos < 0) + return -EINVAL; + + file->f_pos = newpos; + return newpos; +} + +extern __u8 nfc_1k_page_size; +extern __u8 nfc_1k_ecc_mode; + +static ssize_t rawnand_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + struct __PhysicOpPara_t op; + struct boot_physical_param pp; + loff_t page = *ppos; + int result, mode; + __u8 old_ecc_mode, old_page_size; + const char *msg; + + if(count != 512 * SECTOR_CNT_OF_SINGLE_PAGE) { + printk(KERN_INFO "[RAWNAND] Correct write block size: %d\n", 512 * SECTOR_CNT_OF_SINGLE_PAGE); + return -EINVAL; + } + + mode = page < (32 * 1024); /* 32kB for boot0 */ + + if(do_div(page, 512 * SECTOR_CNT_OF_SINGLE_PAGE)) { + printk(KERN_INFO "[RAWNAND] Read address not page aligned: %ld\n", *ppos); + return -EINVAL; + } + + if(!rawnand_xferbuf) + rawnand_xferbuf = kmalloc(count * 2, GFP_KERNEL); + if(!rawnand_xferbuf) { + printk(KERN_INFO "[RAWNAND] Failed to allocate kernel transfer buffer.\n"); + return -ENOMEM; + } + + if(copy_from_user(rawnand_xferbuf, buf, count)) + return -EFAULT; + + op.BankNum = 0; + op.PageNum = do_div(page, PAGE_CNT_OF_PHY_BLK); + op.BlkNum = page; + op.SectBitmap = FULL_BITMAP_OF_SINGLE_PAGE; + op.MDataPtr = rawnand_xferbuf; + op.SDataPtr = NULL; + + pp.chip = 0; + pp.block = op.BlkNum; + pp.page = op.PageNum; + pp.sectorbitmap = FULL_BITMAP_OF_SINGLE_PAGE; + pp.mainbuf = rawnand_xferbuf; + pp.oobbuf = NULL; + + if(!op.PageNum) { + result = PHY_SimpleErase(&pp); + if(result) { + printk(KERN_INFO "[RAWNAND] Erase of block %d failed with code %d%s.\n", pp.block, result, ""); + return result; + } + printk(KERN_DEBUG "[RAWNAND] Erase of block %d successful.\n", pp.block); + } + + msg = "Write"; + if(mode) { + old_page_size = nfc_1k_page_size; + nfc_1k_page_size = SECTOR_CNT_OF_SINGLE_PAGE >> 1; + + old_ecc_mode = nfc_1k_ecc_mode; + if(SECTOR_CNT_OF_SINGLE_PAGE == 16) + nfc_1k_ecc_mode = 1; + + result = PHY_SimpleWrite_1K(&pp); + if(!result) { + pp.mainbuf = rawnand_xferbuf + count; + result = PHY_SimpleRead_1K(&pp); + if(result) + msg = "Readback"; + } + if(!result && memcmp(rawnand_xferbuf, rawnand_xferbuf + count, count)) { + result = -ENOSPC; + msg = "Verification"; + } + + if(SECTOR_CNT_OF_SINGLE_PAGE == 16) + nfc_1k_ecc_mode = old_ecc_mode; + + nfc_1k_page_size = old_page_size; + } else { + result = PHY_SimpleWrite(&pp); + if(!result) { + pp.mainbuf = rawnand_xferbuf + count; + result = PHY_SimpleRead(&pp); + if(result) + msg = "Readback"; + } + if(!result && memcmp(rawnand_xferbuf, rawnand_xferbuf + count, count)) { + result = -ENOSPC; + msg = "Verification"; + } + } + + if(result) { + printk(KERN_INFO "[RAWNAND] %s of page %d.%d failed with code %d%s.\n", msg, pp.block, pp.page, result, ""); + return result; + } + printk(KERN_DEBUG "[RAWNAND] Write of page %d.%d successful.\n", pp.block, pp.page); + + *ppos += count; + + return count; +} + +static ssize_t rawnand_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct __PhysicOpPara_t op; + struct boot_physical_param pp; + loff_t page = *ppos; + int result, mode; + __u8 old_ecc_mode, old_page_size; + + if(count != 512 * SECTOR_CNT_OF_SINGLE_PAGE) { + printk(KERN_INFO "[RAWNAND] Correct read block size: %d\n", 512 * SECTOR_CNT_OF_SINGLE_PAGE); + return -EINVAL; + } + + mode = page < (32 * 1024); /* 32kB for boot0 */ + + if(do_div(page, 512 * SECTOR_CNT_OF_SINGLE_PAGE)) { + printk(KERN_INFO "[RAWNAND] Read address not page aligned: %ld\n", *ppos); + return -EINVAL; + } + + if(!rawnand_xferbuf) + rawnand_xferbuf = kmalloc(count * 2, GFP_KERNEL); + if(!rawnand_xferbuf) { + printk(KERN_INFO "[RAWNAND] Failed to allocate kernel transfer buffer.\n"); + return -ENOMEM; + } + + op.BankNum = 0; + op.PageNum = do_div(page, PAGE_CNT_OF_PHY_BLK); + op.BlkNum = page; + op.SectBitmap = FULL_BITMAP_OF_SINGLE_PAGE; + op.MDataPtr = rawnand_xferbuf; + op.SDataPtr = NULL; + + pp.chip = 0; + pp.block = op.BlkNum; + pp.page = op.PageNum; + pp.sectorbitmap = FULL_BITMAP_OF_SINGLE_PAGE; + pp.mainbuf = rawnand_xferbuf; + pp.oobbuf = NULL; + + if(mode) { + old_page_size = nfc_1k_page_size; + nfc_1k_page_size = SECTOR_CNT_OF_SINGLE_PAGE >> 1; + + /* A20 boot ROM only allows 24 or 40 bit ECC for 8k pages */ + if(SECTOR_CNT_OF_SINGLE_PAGE == 16) { + old_ecc_mode = nfc_1k_ecc_mode; + nfc_1k_ecc_mode = 4; + result = PHY_SimpleRead_1K(&pp); + if(result) { + nfc_1k_ecc_mode = 1; + result = PHY_SimpleRead_1K(&pp); + } + nfc_1k_ecc_mode = old_ecc_mode; + } else + result = PHY_SimpleRead_1K(&pp); + + nfc_1k_page_size = old_page_size; + } else + result = PHY_SimpleRead(&pp); + + if(result) { + printk(KERN_INFO "[RAWNAND] Read of page %d.%d failed with code %d.\n", op.BlkNum, op.PageNum, result); + return result; + } + + if(copy_to_user(buf, rawnand_xferbuf, count)) + return -EFAULT; + + *ppos += count; + + return count; +} + +static const struct file_operations rawnand_fops = { + .owner = THIS_MODULE, + .llseek = rawnand_llseek, + .read = rawnand_read, + .write = rawnand_write, +}; + +static struct miscdevice rawnand_dev = { + MISC_DYNAMIC_MINOR, + "rawnand", + &rawnand_fops, +}; + static int __init init_blklayer(void) { int ret; @@ -1315,7 +1540,6 @@ static int __init init_blklayer(void) } #endif - ret = PHY_ChangeMode(1); if (ret < 0) return ret; @@ -1342,6 +1566,12 @@ static int __init init_blklayer(void) NAND_CacheOpen(); #endif + ret = misc_register(&rawnand_dev); + if(ret) { + printk(KERN_ERR "[RAW-NAND] could not register misc device\n"); + return ret; + } + return nand_blk_register(&mytr); }