522 lines
12 KiB
C
522 lines
12 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/phynand.h"
|
|
#ifndef AML_NAND_UBOOT
|
|
#include<linux/cdev.h>
|
|
#include <linux/device.h>
|
|
|
|
#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); */
|
|
kfree(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;
|
|
} else {
|
|
aml_nand_msg("dtb erase success");
|
|
}
|
|
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); */
|
|
kfree(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__);
|
|
|
|
kfree(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) {
|
|
kfree(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) {
|
|
kfree(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;
|
|
}
|
|
|
|
/* for blank positions... */
|
|
int aml_nand_update_dtb(struct amlnand_chip *aml_chip, char *dtb_ptr)
|
|
{
|
|
int ret = 0;
|
|
char malloc_flag = 0;
|
|
char *dtb_buf = NULL;
|
|
struct nand_flash *flash = &aml_chip->flash;
|
|
|
|
if (dtb_buf == NULL) {
|
|
dtb_buf = kzalloc(aml_chip_dtb->dtbsize + flash->pagesize, GFP_KERNEL);
|
|
malloc_flag = 1;
|
|
if (dtb_buf == NULL)
|
|
return -ENOMEM;
|
|
memset(dtb_buf, 0, aml_chip_dtb->dtbsize);
|
|
ret = amlnand_read_info_by_name(aml_chip,
|
|
(u8 *)&(aml_chip->amlnf_dtb),
|
|
(u8 *)dtb_buf,
|
|
(u8 *)DTD_INFO_HEAD_MAGIC,
|
|
aml_chip_dtb->dtbsize);
|
|
if (ret) {
|
|
aml_nand_msg("read dtb error,%s\n", __func__);
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
} else
|
|
dtb_buf = dtb_ptr;
|
|
|
|
ret = amlnand_save_info_by_name(aml_chip,
|
|
(u8 *)&(aml_chip->amlnf_dtb),
|
|
(u8 *)dtb_buf,
|
|
(u8 *)DTD_INFO_HEAD_MAGIC,
|
|
aml_chip_dtb->dtbsize);
|
|
if (ret < 0)
|
|
aml_nand_msg("%s: update failed", __func__);
|
|
exit:
|
|
if (malloc_flag && (dtb_buf)) {
|
|
kfree(dtb_buf);
|
|
dtb_buf = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|