

#include "../include/phynand.h"

#ifndef AML_NAND_UBOOT
extern void amlnand_release_device(struct amlnand_phydev *phydev);
extern int amlnand_get_device(struct amlnand_phydev *phydev, chip_state_t new_state);
#endif
extern void uboot_set_ran_mode(struct amlnand_phydev *phydev);
extern void nand_boot_info_prepare(struct amlnand_phydev *phydev, unsigned char *page0_buf);

static int read_uboot(struct amlnand_phydev *phydev)
{
	struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
	struct nand_flash *flash = &(aml_chip->flash);
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	struct chip_operation *operation = &(aml_chip->operation);
	struct chip_ops_para *ops_para = &(aml_chip->ops_para);
	struct en_slc_info *slc_info = &(controller->slc_info);

	unsigned configure_data, pages_per_blk, configure_data_w, pages_per_blk_w, page_size, tmp_size;
	unsigned char tmp_bch_mode, tmp_user_mode, tmp_ecc_limit, tmp_ecc_max;
	unsigned short tmp_ecc_unit, tmp_ecc_bytes, tmp_ecc_steps;
	uint64_t addr, readlen = 0, len = 0;
	int  ret = 0;

	if ((devops->addr + devops->len) >  phydev->size) {
		aml_nand_msg("out of space and addr:%llx len:%llx phydev->offset:%llx phydev->size:%llx", \
						devops->addr, devops->len, phydev->offset, phydev->size);
		return -NAND_ARGUMENT_FAILURE;
	}

	if ((devops->len == 0) && (devops->ooblen == 0)) {
		aml_nand_msg("len equal zero here");
		return NAND_SUCCESS;
	}

	tmp_ecc_unit = controller->ecc_unit;
	tmp_ecc_bytes = controller->ecc_bytes;
	tmp_bch_mode = controller->bch_mode;
	tmp_user_mode	= controller->user_mode;
	tmp_ecc_limit = controller->ecc_cnt_limit;
	tmp_ecc_max = controller->ecc_max;
	tmp_ecc_steps = controller->ecc_steps;


	if (controller->bch_mode == NAND_ECC_BCH_SHORT) {
		page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
	}

	tmp_size = phydev->writesize;
	/* phydev->writesize = page_size; */

	amlnand_get_device(phydev, CHIP_READING);

	/* clear ops_para here */
	memset(ops_para, 0, sizeof(struct chip_ops_para));

	if (devops->len == 0) {
		len = phydev->writesize;
		ops_para->ooblen = devops->ooblen;
	}
	else{
		len = devops->len;
		ops_para->ooblen = devops->ooblen;
	}

	addr = phydev->offset + devops->addr;
	ops_para->data_buf = devops->datbuf;
	ops_para->option = phydev->option;
	ops_para->oob_buf = devops->oobbuf;

	if (devops->mode == NAND_SOFT_ECC) {
		ops_para->option |= DEV_ECC_SOFT_MODE;
	}

	if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM)))
		ops_para->option |= DEV_SLC_MODE;

	pages_per_blk = flash->blocksize / flash->pagesize;
	configure_data = NFC_CMD_N2M(controller->ran_mode, controller->bch_mode, 0, (controller->ecc_unit >> 3), controller->ecc_steps);
	while (1) {
		if (((addr / flash->pagesize)%BOOT_PAGES_PER_COPY) == 0) {
				uboot_set_ran_mode(phydev);
				page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
				phydev->writesize = page_size;
#if 1
				controller->ecc_unit = NAND_ECC_UNIT_SHORT;
				controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
				controller->bch_mode = NAND_ECC_BCH_SHORT;
				controller->user_mode = 2;
				controller->ecc_cnt_limit = 55;
				controller->ecc_max = 60;
				controller->ecc_steps = (flash->pagesize+flash->oobsize)/512;
#endif

		} else{
			controller->ran_mode = 1;
		}

		ops_para->page_addr = (int)(addr / flash->pagesize);/* /controller->chip_num; */
		if ((ops_para->option & DEV_SLC_MODE)) {
			if ((flash->new_type > 0) && (flash->new_type < 10))
				ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | (slc_info->pagelist[ops_para->page_addr % 256]);
			if (flash->new_type == SANDISK_19NM)
				ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | ((ops_para->page_addr % pages_per_blk) << 1);
		}

		ret = operation->read_page(aml_chip);
		if ((ops_para->ecc_err) || (ret < 0)) {
			aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
			break;
		}

		/* check info page */
		if ((!strncmp((char *)phydev->name, NAND_BOOT_NAME, strlen((const char *)NAND_BOOT_NAME)))
				&& (((addr / flash->pagesize)%BOOT_PAGES_PER_COPY) == 0)) {

			controller->ran_mode = 1;  /* 1 */


			memcpy((unsigned char *)(&configure_data_w), ops_para->data_buf, sizeof(int));
			memcpy((unsigned char *)(&pages_per_blk_w), ops_para->data_buf+4, sizeof(int));

			aml_nand_msg("configure_data:%x, pages_per_blk:%x", configure_data, pages_per_blk);

			addr += flash->pagesize;
#if 1
			controller->ecc_unit = tmp_ecc_unit;
			controller->ecc_bytes = tmp_ecc_bytes;
			controller->bch_mode = tmp_bch_mode;
			controller->user_mode = tmp_user_mode;
			controller->ecc_cnt_limit = tmp_ecc_limit;
			controller->ecc_max = tmp_ecc_max;
			controller->ecc_steps = tmp_ecc_steps;
			phydev->writesize = tmp_size;
#endif
			continue;
		}

		addr += flash->pagesize;
		ops_para->data_buf += phydev->writesize;
		readlen += phydev->writesize;

		if (readlen >= len) {
			break;
		}
	}

	devops->retlen = readlen;

	amlnand_release_device(phydev);

	if (!ret) {
		if (ops_para->ecc_err) {
			ret = NAND_ECC_FAILURE;
		}
		/*else if(ops_para->bit_flip){
			ret = NAND_BITFLIP_FAILURE;
		}*/
	}

	controller->ran_mode = 1;
	controller->ecc_unit = tmp_ecc_unit;
	controller->ecc_bytes = tmp_ecc_bytes;
	controller->bch_mode = tmp_bch_mode;
	controller->user_mode = tmp_user_mode;
	controller->ecc_cnt_limit = tmp_ecc_limit;
	controller->ecc_max = tmp_ecc_max;
	controller->ecc_steps = tmp_ecc_steps;
	phydev->writesize = tmp_size;

	return ret;
}

int roomboot_nand_read(struct amlnand_phydev *phydev)
{
	struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	/* struct chip_operation *operation = &(aml_chip->operation); */
	/* struct chip_ops_para *ops_para = &(aml_chip->ops_para); */

	uint64_t offset , write_len;
	unsigned char *buffer, tmp_user_mode = 0;
	int ret = 0;
	int oob_set = 0;

	offset = devops->addr;
	write_len = devops->len;
	buffer = devops->datbuf;

	memset(devops, 0x0, sizeof(struct phydev_ops));
	devops->addr = offset;
	devops->len = write_len;
	devops->datbuf = buffer;
	devops->oobbuf = NULL;
	devops->mode = NAND_HW_ECC;
	if (controller->oob_mod) {
		oob_set = controller->oob_mod;
		NFC_CLR_OOB_MODE(3<<26);
		controller->oob_mod = 0;
		tmp_user_mode = controller->user_mode;
		controller->user_mode = 2;
	}
	ret = read_uboot(phydev);
	if (ret < 0) {
		aml_nand_dbg("nand read failed at %llx", devops->addr);
	}
	if (oob_set) {
		controller->oob_mod = oob_set;
		NFC_SET_OOB_MODE(3<<26);
		controller->user_mode = tmp_user_mode;
	}
	return ret;
}

static int write_uboot(struct amlnand_phydev *phydev)
{
	struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
	struct nand_flash *flash = &(aml_chip->flash);
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	struct chip_operation *operation = &(aml_chip->operation);
	struct chip_ops_para *ops_para = &(aml_chip->ops_para);
	struct en_slc_info *slc_info = &(controller->slc_info);
	unsigned char *fill_buf = NULL;
	unsigned char *oob_buf = NULL, *page0_buf = NULL, tmp_bch_mode, tmp_user_mode, tmp_ecc_limit, tmp_ecc_max, tmp_rand;
	unsigned configure_data, pages_per_blk, oobsize, page_size, tmp_size, priv_lsb, ops_tem;
	unsigned short tmp_ecc_unit, tmp_ecc_bytes, tmp_ecc_steps;
	uint64_t addr, writelen = 0, len = 0;
	/* int chip_num=1, nand_read_info, new_nand_type, i, ret = 0; */
	int i, ret = 0;
	/* unsigned char  *tmp_buf; */
	char write_boot_status[BOOT_COPY_NUM] = {0}, err = 0;

	if ((devops->addr + devops->len) >  phydev->size) {
		aml_nand_msg("writeboot:out of space and addr:%llx len:%llx phydev->offset:%llx phydev->size:%llx", \
						devops->addr, devops->len, phydev->offset, phydev->size);
		return -NAND_ARGUMENT_FAILURE;
	}

	if ((devops->len == 0) && (devops->ooblen == 0)) {
		aml_nand_msg("len equal zero here");
		return NAND_SUCCESS;
	}

	if (devops->addr != 0) {
		aml_nand_msg("addr do not equal zero here for uboot write");
		return NAND_SUCCESS;
	}

	tmp_ecc_unit = controller->ecc_unit;
	tmp_ecc_bytes = controller->ecc_bytes;
	tmp_bch_mode = controller->bch_mode;
	tmp_user_mode	= controller->user_mode;
	tmp_ecc_limit = controller->ecc_cnt_limit;
	tmp_ecc_max = controller->ecc_max;
	tmp_ecc_steps = controller->ecc_steps;
	tmp_rand = controller->ran_mode;
	if (controller->bch_mode == NAND_ECC_BCH_SHORT) {
		page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
	}

	oobsize = controller->ecc_steps*controller->user_mode;

	tmp_size = phydev->writesize;
	/* phydev->writesize = page_size; */

	amlnand_get_device(phydev, CHIP_WRITING);

	oob_buf = aml_nand_malloc(oobsize);
	if (oob_buf == NULL) {
		aml_nand_msg("malloc failed and oobavail:%d", phydev->oobavail);
		ret = -NAND_MALLOC_FAILURE;
		goto error_exit;
	}
	memset(oob_buf, 0x0, oobsize);
	page0_buf = aml_nand_malloc(flash->pagesize);
	if (page0_buf == NULL) {
		aml_nand_msg("malloc failed and oobavail:%d", flash->pagesize);
		ret = -NAND_MALLOC_FAILURE;
		goto error_exit;
	}
	memset(page0_buf, 0x0, flash->pagesize);
	len = devops->len;
	for (i = 0; i < oobsize; i += 2) {
		oob_buf[i] = 0x55;
		oob_buf[i+1] = 0xaa;
	}

	fill_buf = aml_nand_malloc(flash->pagesize);
	if (fill_buf == NULL) {
		aml_nand_msg("malloc failed and oobavail:%d", flash->pagesize);
		ret = -NAND_MALLOC_FAILURE;
		goto error_exit;
	}
	memset(fill_buf, 0xff, flash->pagesize);
	/* clear ops_para here */
	memset(ops_para, 0, sizeof(struct chip_ops_para));
	addr = phydev->offset + devops->addr;
	ops_para->option = phydev->option;
	ops_para->data_buf = devops->datbuf;
	ops_para->oob_buf = oob_buf;

	if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM))) {
	ops_para->option |= DEV_SLC_MODE;
	}

	configure_data = NFC_CMD_N2M(controller->ran_mode, controller->bch_mode, 0, (controller->ecc_unit >> 3), controller->ecc_steps);

	/* configure_data = NFC_CMD_N2M(controller->ran_mode, NAND_ECC_BCH60_1K, 1, (controller->ecc_unit >> 3), controller->ecc_steps); */
	pages_per_blk = flash->blocksize / flash->pagesize;
	aml_nand_msg("configure_data:%x, pages_per_blk:%x", configure_data, pages_per_blk);

	nand_boot_info_prepare(phydev, page0_buf);
	unsigned char *lazy_buf = devops->datbuf;
	for (i = 0; i < BOOT_COPY_NUM; i++) {
		writelen = 0;
		addr = 0;
		addr += flash->pagesize*(BOOT_PAGES_PER_COPY*i);
		ops_para->data_buf = lazy_buf;
		devops->datbuf = lazy_buf;
		while (1) {
			if (((addr / flash->pagesize) % BOOT_PAGES_PER_COPY) == 0) {
				uboot_set_ran_mode(phydev);
				ops_para->data_buf = page0_buf;

				page_size = (flash->pagesize / 512) * NAND_ECC_UNIT_SHORT;
				phydev->writesize = page_size;
#if 1
				controller->ecc_unit = NAND_ECC_UNIT_SHORT;
				controller->ecc_bytes = NAND_BCH60_1K_ECC_SIZE;
				controller->bch_mode = NAND_ECC_BCH_SHORT;
				controller->user_mode = 2;
				controller->ecc_cnt_limit = 55;
				controller->ecc_max = 60;
				controller->ecc_steps = (flash->pagesize+flash->oobsize)/512;
#endif
			}
			ops_para->page_addr = (int)(addr / flash->pagesize);/* /controller->chip_num; */
			ops_tem = ops_para->page_addr;
			if ((ops_para->option & DEV_SLC_MODE)) {
				if ((flash->new_type > 0) && (flash->new_type < 10))
					ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | (slc_info->pagelist[ops_para->page_addr % 256]);
				if (flash->new_type == SANDISK_19NM)
					ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | ((ops_para->page_addr % pages_per_blk) << 1);
			}
#if 1
			if (flash->new_type == HYNIX_1YNM) {
				if ((ops_tem % 256) > 1) {
					priv_lsb =  (ops_tem & (~(pages_per_blk - 1))) | (slc_info->pagelist[(ops_tem % 256)-1]);
				ops_tem = ops_para->page_addr;
				while (ops_tem > (priv_lsb+1)) {
				ops_para->data_buf = fill_buf;
				controller->bch_mode = NAND_ECC_NONE;
				controller->ran_mode = 0;
				ops_para->page_addr = priv_lsb+1;
				 operation->write_page(aml_chip);
				 priv_lsb++;
					}
				ops_para->page_addr = ops_tem;
				ops_para->data_buf = devops->datbuf;
				controller->ecc_unit = tmp_ecc_unit;
				controller->ecc_bytes = tmp_ecc_bytes;
				controller->bch_mode = tmp_bch_mode;
				controller->user_mode = tmp_user_mode;
				controller->ecc_cnt_limit = tmp_ecc_limit;
				controller->ecc_max = tmp_ecc_max;
				controller->ecc_steps = tmp_ecc_steps;
				phydev->writesize = tmp_size;
				controller->ran_mode = tmp_rand;
				}
			}
	#endif
			if (((addr / flash->pagesize) % BOOT_PAGES_PER_COPY) != 0)
					ops_para->data_buf = devops->datbuf;
			ret = operation->write_page(aml_chip);
			if (ret < 0) {
				write_boot_status[i] = 1;
				aml_nand_msg("fail page_addr:%d", ops_para->page_addr);
				break;
			}

			if (((addr / flash->pagesize) % BOOT_PAGES_PER_COPY) == 0) {
				controller->ran_mode = 1;
				addr += flash->pagesize;
				ops_para->data_buf = devops->datbuf;
#if 1
				controller->ecc_unit = tmp_ecc_unit;
				controller->ecc_bytes = tmp_ecc_bytes;
				controller->bch_mode = tmp_bch_mode;
				controller->user_mode = tmp_user_mode;
				controller->ecc_cnt_limit = tmp_ecc_limit;
				controller->ecc_max = tmp_ecc_max;
				controller->ecc_steps = tmp_ecc_steps;
				phydev->writesize = tmp_size;
#endif
				continue;
			}

			addr += flash->pagesize;
			devops->datbuf += phydev->writesize;
			writelen += phydev->writesize;

			if ((writelen >= devops->len) && (writelen < phydev->erasesize)) {
				devops->datbuf = fill_buf;
			}
		
			/*write down all the boot length, we should fill garbage data to the whole block.*/
			if ((writelen >= (len-flash->pagesize)) \
				&&(((ops_para->option & DEV_SLC_MODE) &&
					((unsigned)addr%(flash->blocksize>>1) == 0)) \
				|| (((ops_para->option & DEV_SLC_MODE) == 0) &&
				((unsigned)addr%flash->blocksize == 0)))) {
			        break;
			}			
			
		}
	}
	for (i = 0; i < BOOT_COPY_NUM; i++) {

		err += write_boot_status[i];
	}
	if (err < 2)
		ret = 0;
	else
		ret = 1;
	devops->retlen = writelen;

error_exit:
	amlnand_release_device(phydev);

	controller->ran_mode = 1;
	controller->ecc_unit = tmp_ecc_unit;
	controller->ecc_bytes = tmp_ecc_bytes;
	controller->bch_mode = tmp_bch_mode;
	controller->user_mode = tmp_user_mode;
	controller->ecc_cnt_limit = tmp_ecc_limit;
	controller->ecc_max = tmp_ecc_max;
	controller->ecc_steps = tmp_ecc_steps;
	phydev->writesize = tmp_size;

	if (page0_buf) {
		kfree(page0_buf);
		page0_buf = NULL;
	}
	if (fill_buf) {
		kfree(fill_buf);
		fill_buf = NULL;
	}
	if (oob_buf) {
		kfree(oob_buf);
		oob_buf = NULL;
	}

	return ret;
}

int roomboot_nand_write(struct amlnand_phydev *phydev)
{
	struct amlnand_chip *aml_chip = (struct amlnand_chip *)phydev->priv;
	struct nand_flash *flash = &aml_chip->flash;
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	struct chip_operation *operation = &(aml_chip->operation);
	struct chip_ops_para *ops_para = &(aml_chip->ops_para);

	uint64_t offset , write_len, addr;
	unsigned char *buffer, tmp_user_mode = 0;
	int pages_per_blk = 0, ret = 0;
	int oob_set = 0;
	offset = devops->addr;
	write_len = devops->len;
	buffer = devops->datbuf;

	if (offset != 0) {
		aml_nand_msg("Wrong addr begin");
		return -1;
	}
	/*if(write_len < 0x60000){
		aml_nand_msg("Wrong size begin");
		write_len = 0x60000;
	}
	write_len = (((write_len +phydev->writesize)-1)/phydev->writesize)*phydev->writesize;
	*/
	if (write_len < phydev->erasesize) {
		write_len = phydev->erasesize;
	}
	if ((flash->new_type) && ((flash->new_type < 10) || (flash->new_type == SANDISK_19NM))) {
		write_len =  phydev->erasesize >> 1;
	}
	write_len = ((((unsigned)write_len + phydev->writesize)-1)/phydev->writesize)*phydev->writesize;

	if ((offset & (phydev->writesize - 1)) != 0 || (write_len & (phydev->writesize - 1)) != 0) {
		printf("Attempt to write non page aligned data\n");
		return -NAND_WRITE_FAILED;
	}

	if ((offset + write_len) > (phydev->size / BOOT_COPY_NUM)) {
		aml_nand_dbg("Attemp to write out side the dev area");
		return -NAND_WRITE_FAILED;
	}
	if (controller->oob_mod) {

		oob_set = controller->oob_mod;
		NFC_CLR_OOB_MODE(3<<26);
		controller->oob_mod = 0;
		tmp_user_mode = controller->user_mode;
		controller->user_mode = 2;
	}
	pages_per_blk = flash->blocksize / flash->pagesize;
	memset(ops_para, 0, sizeof(struct chip_ops_para));
	addr = phydev->offset + devops->addr;
	ops_para->chipnr = 0;
	ops_para->option = phydev->option;
	if ((flash->new_type) && (flash->new_type == SANDISK_19NM))
	ops_para->option |= DEV_SLC_MODE;

	do {
			ops_para->page_addr = (unsigned)(addr / flash->pagesize);
			controller->select_chip(controller, ops_para->chipnr);
			ret = operation->block_isbad(aml_chip);
			if (ret ==  NAND_BLOCK_FACTORY_BAD) {
				aml_nand_msg("nand blk is shipped bad block at page %d", ops_para->page_addr);
				addr += phydev->erasesize;
				continue;
			}

			if ((flash->new_type == SANDISK_19NM) && (ops_para->option & DEV_SLC_MODE)) {
				ops_para->page_addr = (ops_para->page_addr & (~(pages_per_blk - 1))) | ((ops_para->page_addr % pages_per_blk) << 1);
			}
#ifdef AML_NAND_UBOOT
			nand_get_chip();
#else
			nand_get_chip(aml_chip);
#endif
			ret = operation->erase_block(aml_chip);
#ifdef AML_NAND_UBOOT
			nand_release_chip();
#else
			nand_release_chip(aml_chip);
#endif

			if (ret < 0) {
				aml_nand_msg("nand erase fail at addr :%lx ", (long unsigned int)(ops_para->page_addr));
				/* break; */
			}

			addr += phydev->erasesize;
		} while (addr < (1024 * flash->pagesize));

	memset(devops, 0x0, sizeof(struct phydev_ops));
	devops->addr = offset;
	devops->len = write_len;
	devops->datbuf = buffer;
	devops->oobbuf = NULL;
	devops->mode = NAND_HW_ECC;

	aml_nand_dbg("devops->addr =%llx", devops->addr);
	aml_nand_dbg("devops->len =%llx", devops->len);
	ret = write_uboot(phydev);
	if (ret < 0) {
		aml_nand_dbg("nand write failed at %llx", devops->addr);
		goto exit_error0;
	}
	if (oob_set) {
		controller->oob_mod = oob_set;
		NFC_SET_OOB_MODE(3<<26);
		controller->user_mode = tmp_user_mode;
	}
	return ret;

exit_error0:
	return ret;
}

#ifndef AML_NAND_UBOOT
struct device *devp;
static dev_t uboot_devno;
struct amlnand_phydev *uboot_phydev  = NULL;
#define UBOOT_WRITE_SIZE  0x60000

static int uboot_open(struct inode *inode, struct file *filp)
{
	aml_nand_dbg("uboot_open");
	return 0;
}
/*
 * This funcion reads the u-boot envionment variables.
 * The f_pos points directly to the env location.
 */
static ssize_t uboot_read(struct file *file, char __user *buf,
			size_t count, loff_t *ppos)
{
		return 0;
}

static ssize_t uboot_write(struct file *file, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	struct amlnand_phydev *phydev = uboot_phydev;
	struct amlnand_chip *aml_chip = phydev->priv;
	struct nand_flash *flash = &(aml_chip->flash);
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	struct chip_operation *operation = &(aml_chip->operation);
	struct chip_ops_para *ops_para = &(aml_chip->ops_para);

	unsigned char *data_buf;
	int  ret;
	size_t align_count = 0;
	/* data_buf = aml_nand_malloc(UBOOT_WRITE_SIZE); */
	align_count = ((((unsigned)count + phydev->writesize)-1)/phydev->writesize)*phydev->writesize;
	data_buf = aml_nand_malloc(align_count);
	if (!data_buf) {
		aml_nand_dbg("malloc buf for rom_write failed");
		goto err_exit0;
	}
	/* memset(data_buf,0x0,UBOOT_WRITE_SIZE); */
	/* ret=copy_from_user(data_buf, buf, UBOOT_WRITE_SIZE); */
	memset(data_buf, 0x0, align_count);
	ret = copy_from_user(data_buf, buf, count);

	memset(devops, 0x0, sizeof(struct phydev_ops));
	devops->addr = 0x0;
	devops->len = align_count;
	devops->mode = NAND_HW_ECC;
	devops->datbuf = data_buf;

	ret = roomboot_nand_write(phydev);
	if (ret < 0) {
		aml_nand_dbg("uboot_write failed");
		count = 0;
	}

err_exit0:

	if (data_buf) {
		aml_nand_free(data_buf);
		data_buf = NULL;
	}

	return count;
}

static int uboot_close(struct inode *inode, struct file *file)
{
	return 0;
}
static int uboot_suspend(struct device *dev, pm_message_t state)
{
		return 0;
}

static int uboot_resume(struct device *dev)
{
	return 0;
}

static struct class uboot_class = {

	.name = "bootloader",
	.owner = THIS_MODULE,
	.suspend = uboot_suspend,
	.resume = uboot_resume,
};

static struct file_operations uboot_fops = {

	.owner = THIS_MODULE,
	.open = uboot_open,
	.read = uboot_read,
	.write = uboot_write,
	.release = uboot_close,
};

int boot_device_register(struct amlnand_phydev *phydev)
{
	struct amlnand_chip *aml_chip = phydev->priv;
	struct nand_flash *flash = &(aml_chip->flash);
	struct phydev_ops *devops = &(phydev->ops);
	struct hw_controller *controller = &(aml_chip->controller);
	struct chip_operation *operation = &(aml_chip->operation);
	struct chip_ops_para *ops_para = &(aml_chip->ops_para);

	int ret = 0;

	if (!strncmp((char *)phydev->name, NAND_BOOT_NAME, strlen((const char *)NAND_BOOT_NAME))) {

			aml_nand_dbg("boot device register");

			uboot_phydev = phydev;
			ret = alloc_chrdev_region(&uboot_devno, 0, 1, "bootloader");
			if (ret < 0) {
				aml_nand_dbg("uboot_phydev: failed to allocate chrdev. ");
				goto exit_error0;
			}
			cdev_init(&uboot_phydev->uboot_cdev, &uboot_fops);
			uboot_phydev->uboot_cdev.owner = THIS_MODULE;
			ret = cdev_add(&uboot_phydev->uboot_cdev, uboot_devno, 1);
			if (ret) {
				aml_nand_dbg("uboot_phydev: failed to add device.");
				goto exit_error0;
			}

			ret = class_register(&uboot_class);
			if (ret < 0) {
				aml_nand_dbg("class_register(&uboot_class) failed!\n");
				goto exit_error0;
			}

			devp = device_create(&uboot_class, NULL, uboot_devno, NULL, "bootloader");
			if (IS_ERR(devp)) {
				aml_nand_dbg("uboot_phydev: failed to create device node\n");
				ret = PTR_ERR(devp);
				goto exit_error0;
			}
		}

		return NAND_SUCCESS;

exit_error0:
	return ret;
}
#endif

