/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/nand/dev/amlnf_dtb.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include "../include/phynand.h" #ifndef AML_NAND_UBOOT #include #include #define DTB_NAME "amlnf_dtb" static dev_t amlnf_dtb_no; struct cdev amlnf_dtb; struct device *dtb_dev = NULL; struct class *amlnf_dtb_class = NULL; #endif /* AML_NAND_UBOOT */ int dtb_erase_blk = -1; extern int get_partition_from_dts(unsigned char * buffer); struct amlnand_chip *aml_chip_dtb = NULL; int amlnf_dtb_save(u8 *buf, int len) { u8 *dtb_buf = NULL; struct nand_flash *flash = &aml_chip_dtb->flash; int ret = 0; aml_nand_msg("%s: ####", __func__); if (aml_chip_dtb == NULL) { aml_nand_msg("%s: amlnf not init yet!", __func__); return -EFAULT; } if (len > aml_chip_dtb->dtbsize) { aml_nand_msg("warning!!! %s: length too much", __func__); len = aml_chip_dtb->dtbsize; /*return -EFAULT;*/ } dtb_buf = aml_nand_malloc(aml_chip_dtb->dtbsize + flash->pagesize); if (dtb_buf == NULL) { aml_nand_msg("%s: malloc failed", __func__); ret = -1; goto exit_err; } memset(dtb_buf, 0, aml_chip_dtb->dtbsize); memcpy(dtb_buf, buf, len); ret = amlnand_save_info_by_name(aml_chip_dtb, (u8 *)&(aml_chip_dtb->amlnf_dtb), dtb_buf, (u8 *)DTD_INFO_HEAD_MAGIC, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: nand dtd save failed", __func__); ret = -EFAULT; goto exit_err; } exit_err: if (dtb_buf) { /* kfree(dtb_buf); */ aml_nand_free(dtb_buf); dtb_buf = NULL; } return ret; } int amlnf_dtb_erase(void) { int ret = 0; if (aml_chip_dtb == NULL) { aml_nand_msg("%s amlnf not ready yet!", __func__); return -1; } ret = amlnand_erase_info_by_name(aml_chip_dtb, (u8 *)&(aml_chip_dtb->amlnf_dtb), (u8 *)DTD_INFO_HEAD_MAGIC); if (ret) { aml_nand_msg("dtb error,%s", __func__); ret = -EFAULT; } return ret; } int amlnf_dtb_read(u8 *buf, int len) { u8 *dtb_buf = NULL; int ret = 0; aml_nand_msg("%s: ####", __func__); if (len > aml_chip_dtb->dtbsize) { aml_nand_msg("warning!!! %s dtd length too much", __func__); len = aml_chip_dtb->dtbsize; /*return -EFAULT;*/ } if (aml_chip_dtb == NULL) { memset(buf, 0x0, len); aml_nand_msg("%s amlnf not ready yet!", __func__); return 0; } if (aml_chip_dtb->amlnf_dtb.arg_valid == 0) { memset(buf, 0x0, len); aml_nand_msg("%s arg_valid = 0 invalid", __func__); return 0; } dtb_buf = aml_nand_malloc(aml_chip_dtb->dtbsize); if (dtb_buf == NULL) { aml_nand_msg("%s: malloc failed", __func__); ret = -1; goto exit_err; } memset(dtb_buf, 0, aml_chip_dtb->dtbsize); ret = amlnand_read_info_by_name(aml_chip_dtb, (u8 *)&(aml_chip_dtb->amlnf_dtb), (u8 *)dtb_buf, (u8 *)DTD_INFO_HEAD_MAGIC, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("dtb error,%s", __func__); ret = -EFAULT; goto exit_err; } memcpy(buf, dtb_buf, len); exit_err: if (dtb_buf) { /* kfree(dtb_buf); */ aml_nand_free(dtb_buf); dtb_buf = NULL; } return ret; } /* under kernel */ #ifndef AML_NAND_UBOOT ssize_t dtb_show(struct class *class, struct class_attribute *attr, char *buf) { aml_nand_dbg("dtb_show : #####"); /* fixme, read back and show in log! */ return 0; } ssize_t dtb_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int ret = 0; u8 *dtb_ptr = NULL; aml_nand_dbg("dtb_store : #####"); dtb_ptr = kzalloc(aml_chip_dtb->dtbsize, GFP_KERNEL); if (dtb_ptr == NULL) { aml_nand_msg("%s: malloc buf failed ", __func__); return -ENOMEM; } /* fixme, why read back then write? */ ret = amlnf_dtb_read(dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: read failed", __func__); kfree(dtb_ptr); return -EFAULT; } ret = amlnf_dtb_save(dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: save failed", __func__); kfree(dtb_ptr); return -EFAULT; } aml_nand_dbg("dtb_store : OK #####"); return count; } static CLASS_ATTR(amlnf_dtb, S_IWUSR | S_IRUGO, dtb_show, dtb_store); int dtb_open(struct inode *node, struct file *file) { return 0; } ssize_t dtb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { u8 *dtb_ptr = NULL; struct nand_flash *flash = &aml_chip_dtb->flash; ssize_t read_size = 0; int ret = 0; if (*ppos == aml_chip_dtb->dtbsize) return 0; if (*ppos >= aml_chip_dtb->dtbsize) { aml_nand_msg("%s:data access out of space!", __func__); return -EFAULT; } dtb_ptr = vmalloc(aml_chip_dtb->dtbsize + flash->pagesize); if (dtb_ptr == NULL) { aml_nand_msg("%s: malloc buf failed ", __func__); return -ENOMEM; } amlnand_get_device(aml_chip_dtb, CHIP_READING); ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: read failed:%d", __func__, ret); ret = -EFAULT; goto exit; } if ((*ppos + count) > aml_chip_dtb->dtbsize) read_size = aml_chip_dtb->dtbsize - *ppos; else read_size = count; ret = copy_to_user(buf, (dtb_ptr + *ppos), read_size); *ppos += read_size; exit: amlnand_release_device(aml_chip_dtb); /* kfree(dtb_ptr); */ vfree(dtb_ptr); return read_size; } ssize_t dtb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { u8 *dtb_ptr = NULL; ssize_t write_size = 0; struct nand_flash *flash = &aml_chip_dtb->flash; int ret = 0; if (*ppos == aml_chip_dtb->dtbsize) return 0; if (*ppos >= aml_chip_dtb->dtbsize) { aml_nand_msg("%s: data access out of space!", __func__); return -EFAULT; } dtb_ptr = vmalloc(aml_chip_dtb->dtbsize + flash->pagesize); if (dtb_ptr == NULL) { aml_nand_msg("%s: malloc buf failed ", __func__); return -ENOMEM; } amlnand_get_device(aml_chip_dtb, CHIP_WRITING); ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: read failed", __func__); ret = -EFAULT; goto exit; } if ((*ppos + count) > aml_chip_dtb->dtbsize) write_size = aml_chip_dtb->dtbsize - *ppos; else write_size = count; ret = copy_from_user((dtb_ptr + *ppos), buf, write_size); ret = amlnf_dtb_save(dtb_ptr, aml_chip_dtb->dtbsize); if (ret) { aml_nand_msg("%s: read failed", __func__); ret = -EFAULT; goto exit; } *ppos += write_size; exit: amlnand_release_device(aml_chip_dtb); /* kfree(dtb_ptr); */ vfree(dtb_ptr); return write_size; } long dtb_ioctl(struct file *file, u32 cmd, u32 args) { return 0; } static const struct file_operations dtb_ops = { .open = dtb_open, .read = dtb_read, .write = dtb_write, .unlocked_ioctl = dtb_ioctl, }; #endif /* AML_NAND_UBOOT */ int amlnf_dtb_init(struct amlnand_chip *aml_chip) { int ret = 0; u8 *dtb_buf = NULL; aml_chip_dtb = aml_chip; dtb_buf = aml_nand_malloc(aml_chip_dtb->dtbsize); if (dtb_buf == NULL) { aml_nand_msg("nand malloc for dtb_buf failed"); ret = -1; goto exit_err; } memset(dtb_buf, 0x0, aml_chip_dtb->dtbsize); aml_nand_msg("nand dtb: probe."); ret = amlnand_info_init(aml_chip, (u8 *)&(aml_chip->amlnf_dtb), dtb_buf, (u8 *)DTD_INFO_HEAD_MAGIC, aml_chip_dtb->dtbsize); if (ret < 0) { aml_nand_msg("%s failed", __func__); ret = -1; goto exit_err; } #ifndef AML_NAND_UBOOT aml_nand_dbg("%s: register dtb cdev", __func__); ret = alloc_chrdev_region(&amlnf_dtb_no, 0, 1, DTB_NAME); if (ret < 0) { aml_nand_msg("%s alloc dtb dev_t number failed", __func__); ret = -1; goto exit_err; } cdev_init(&amlnf_dtb, &dtb_ops); amlnf_dtb.owner = THIS_MODULE; ret = cdev_add(&amlnf_dtb, amlnf_dtb_no, 1); if (ret) { aml_nand_msg("%s amlnf dtd dev add failed", __func__); ret = -1; goto exit_err1; } amlnf_dtb_class = class_create(THIS_MODULE, DTB_NAME); if (IS_ERR(amlnf_dtb_class)) { aml_nand_msg("%s: amlnf dtd class add failed", __func__); ret = -1; goto exit_err2; } ret = class_create_file(amlnf_dtb_class, &class_attr_env); if (ret) { aml_nand_msg("%s dev add failed", __func__); ret = -1; goto exit_err2; } dtb_dev = device_create(amlnf_dtb_class, NULL, amlnf_dtb_no, NULL, DTB_NAME); if (IS_ERR(dtb_dev)) { aml_nand_msg("%s: device_create failed", __func__); ret = -1; goto exit_err3; } aml_nand_dbg("%s: register dtd cdev OK", __func__); aml_nand_free(dtb_buf); dtb_buf = NULL; return ret; exit_err3: class_remove_file(amlnf_dtb_class, &class_attr_env); class_destroy(amlnf_dtb_class); exit_err2: cdev_del(&amlnf_dtb); exit_err1: unregister_chrdev_region(amlnf_dtb_no, 1); #endif /* AML_NAND_UBOOT */ exit_err: if (dtb_buf) { aml_nand_free(dtb_buf); dtb_buf = NULL; } return ret; } int amlnf_dtb_init_partitions(struct amlnand_chip *aml_chip) { int ret = 0; u8 *dtb_buf = NULL; aml_chip_dtb = aml_chip; dtb_buf = aml_nand_malloc(aml_chip_dtb->dtbsize); if (dtb_buf == NULL) { aml_nand_msg("nand malloc for dtb_buf failed"); ret = -1; goto exit_err; } memset(dtb_buf, 0x0, aml_chip_dtb->dtbsize); aml_nand_msg("%s: probe. ", __func__); ret = amlnand_info_init(aml_chip, (u8 *)&(aml_chip->amlnf_dtb), dtb_buf, (u8 *)DTD_INFO_HEAD_MAGIC, aml_chip_dtb->dtbsize); if (ret < 0) { aml_nand_msg("%s failed", __func__); ret = -1; goto exit_err; } /*parse partitions table */ ret = get_partition_from_dts(dtb_buf); if (ret) { aml_nand_msg("%s get_partition_from_dts failed", __func__); } exit_err: if (dtb_buf) { aml_nand_free(dtb_buf); dtb_buf = NULL; } return ret; } /***************************************************************************** Prototype : amlnf_detect_dtb_partitions Description : if 'dtb, write the bad block, we can't erase this block. So we have to find the 'dtb' address in flash and flag it. Input : struct amlnand_chip *aml_chip Output : NULL Return Value : ret Called By : amlnand_get_partition_table History : 1.Date : 2015/10/15 Author : Fly Mo Modification : Created function *****************************************************************************/ int amlnf_detect_dtb_partitions(struct amlnand_chip *aml_chip) { int ret = 0; u8 *dtb_buf = NULL; aml_chip_dtb = aml_chip; struct nand_arg_info test_amlnf_dtb; memset(&test_amlnf_dtb, 0, sizeof(test_amlnf_dtb)); dtb_erase_blk = -1; dtb_buf = aml_nand_malloc(aml_chip_dtb->dtbsize); if (dtb_buf == NULL) { aml_nand_msg("nand malloc for dtb_buf failed"); ret = -1; goto exit_err; } memset(dtb_buf, 0x0, aml_chip_dtb->dtbsize); test_amlnf_dtb.arg_type = aml_chip->amlnf_dtb.arg_type; ret = amlnand_info_init(aml_chip, (u8 *)&(test_amlnf_dtb), dtb_buf, (u8 *)DTD_INFO_HEAD_MAGIC, aml_chip_dtb->dtbsize); if (test_amlnf_dtb.arg_valid == 1) { dtb_erase_blk = test_amlnf_dtb.valid_blk_addr; } aml_nand_msg("%s:dtb_erase_blk:%d", __func__,dtb_erase_blk); exit_err: if (dtb_buf) { kfree(dtb_buf); dtb_buf = NULL; } return ret; }