blob: 994f8e95461c2ae27b08e0f96003be30797c1120 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/amlogic/aml_rsv.h>
#include <linux/amlogic/aml_mtd_nand.h>
#include <linux/slab.h>
/* protect flag inside */
static int rsv_protect = 1;
static inline void _aml_rsv_disprotect(void)
{
rsv_protect = 0;
}
static inline void _aml_rsv_protect(void)
{
rsv_protect = 1;
}
static inline int _aml_rsv_isprotect(void)
{
return rsv_protect;
}
static struct meson_rsv_handler_t *rsv_handler;
static struct free_node_t *get_free_node(struct meson_rsv_info_t *rsv_info)
{
struct meson_rsv_handler_t *handler = rsv_info->handler;
unsigned long index;
pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__,
handler->freeNodeBitmask);
index = find_first_zero_bit((void *)&handler->freeNodeBitmask,
NAND_RSV_BLOCK_NUM);
if (index >= NAND_RSV_BLOCK_NUM) {
pr_info("%s %d: index is greater than max! error",
__func__, __LINE__);
return NULL;
}
WARN_ON(test_and_set_bit(index, (void *)&handler->freeNodeBitmask));
pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__,
handler->freeNodeBitmask);
return handler->free_node[index];
}
static void release_free_node(struct meson_rsv_info_t *rsv_info,
struct free_node_t *free_node)
{
struct meson_rsv_handler_t *handler = rsv_info->handler;
unsigned int index_save = free_node->index;
pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__,
handler->freeNodeBitmask);
if (index_save > NAND_RSV_BLOCK_NUM) {
pr_info("%s %d: index=%d is greater than max! error",
__func__, __LINE__, index_save);
return;
}
WARN_ON(!test_and_clear_bit(index_save,
(void *)&handler->freeNodeBitmask));
/*memset zero to protect from dead-loop*/
memset(free_node, 0, sizeof(struct free_node_t));
free_node->index = index_save;
pr_info("%s %d: bitmap=%llx\n", __func__, __LINE__,
handler->freeNodeBitmask);
}
int meson_rsv_erase_protect(struct meson_rsv_handler_t *handler,
unsigned int block_addr)
{
if (!_aml_rsv_isprotect())
return 0;
if (handler->bbt && handler->bbt->valid)
if (block_addr >= handler->bbt->start_block &&
block_addr < handler->bbt->end_block)
return -1;/*need skip bbt blocks*/
if (handler->key && handler->key->valid)
if (block_addr >= handler->key->start_block &&
block_addr < handler->key->end_block)
return -1; /*need skip key blocks*/
return 0;
}
int meson_free_rsv_info(struct meson_rsv_info_t *rsv_info)
{
struct mtd_info *mtd = rsv_info->mtd;
struct free_node_t *tmp_node, *next_node = NULL;
int error = 0;
loff_t addr = 0;
struct erase_info erase_info;
pr_info("free %s:\n", rsv_info->name);
if (rsv_info->valid) {
addr = rsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
memset(&erase_info,
0, sizeof(struct erase_info));
erase_info.addr = addr;
erase_info.len = mtd->erasesize;
_aml_rsv_disprotect();
error = mtd->_erase(mtd, &erase_info);
_aml_rsv_protect();
pr_info("erasing valid info block: %llx\n", addr);
rsv_info->valid_node->phy_blk_addr = -1;
rsv_info->valid_node->ec = -1;
rsv_info->valid_node->phy_page_addr = 0;
rsv_info->valid_node->timestamp = 0;
rsv_info->valid_node->status = 0;
rsv_info->valid = 0;
}
tmp_node = rsv_info->free_node;
while (tmp_node) {
next_node = tmp_node->next;
release_free_node(rsv_info, tmp_node);
tmp_node = next_node;
}
rsv_info->free_node = NULL;
return error;
}
int meson_rsv_write(struct meson_rsv_info_t *rsv_info, u_char *buf)
{
struct mtd_info *mtd = rsv_info->mtd;
struct oobinfo_t oobinfo;
struct mtd_oob_ops oob_ops;
size_t length = 0;
loff_t offset;
int ret = 0;
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += rsv_info->valid_node->phy_page_addr * mtd->writesize;
pr_info("%s:%d,save info to %llx\n", __func__, __LINE__, offset);
memcpy(oobinfo.name, rsv_info->name, 4);
oobinfo.ec = rsv_info->valid_node->ec;
oobinfo.timestamp = rsv_info->valid_node->timestamp;
while (length < rsv_info->size) {
oob_ops.mode = MTD_OPS_AUTO_OOB;
oob_ops.len = min_t(u32, mtd->writesize,
(rsv_info->size - length));
oob_ops.ooblen = sizeof(struct oobinfo_t);
oob_ops.ooboffs = 0;
oob_ops.datbuf = buf + length;
oob_ops.oobbuf = (u8 *)&oobinfo;
ret = mtd->_write_oob(mtd, offset, &oob_ops);
if (ret) {
pr_info("blk check good but write failed: %llx, %d\n",
(u64)offset, ret);
return 1;
}
offset += mtd->writesize;
length += oob_ops.len;
}
return ret;
}
int meson_rsv_save(struct meson_rsv_info_t *rsv_info, u_char *buf)
{
struct mtd_info *mtd = rsv_info->mtd;
struct free_node_t *free_node, *temp_node;
struct erase_info erase_info;
int ret = 0, i = 1, pages_per_blk;
loff_t offset = 0;
pages_per_blk = mtd->erasesize / mtd->writesize;
/*solve these abnormals caused by power off and ecc error*/
if (rsv_info->valid_node->status & POWER_ABNORMAL_FLAG ||
rsv_info->valid_node->status & ECC_ABNORMAL_FLAG)
rsv_info->valid_node->phy_page_addr = pages_per_blk;
if (mtd->writesize < rsv_info->size)
i = (rsv_info->size + mtd->writesize - 1) / mtd->writesize;
pr_info("%s:%d, %s: valid=%d, pages=%d\n", __func__, __LINE__,
rsv_info->name, rsv_info->valid, i);
RE_SEARCH:
if (rsv_info->valid) {
rsv_info->valid_node->phy_page_addr += i;
if ((rsv_info->valid_node->phy_page_addr + i) > pages_per_blk) {
if ((rsv_info->valid_node->phy_page_addr - i) ==
pages_per_blk) {
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
memset(&erase_info,
0, sizeof(struct erase_info));
erase_info.addr = offset;
erase_info.len = mtd->erasesize;
_aml_rsv_disprotect();
ret = mtd->_erase(mtd, &erase_info);
_aml_rsv_protect();
if (ret) {
pr_info("rsv free blk erase failed %d\n",
ret);
mtd->_block_markbad(mtd, offset);
}
rsv_info->valid_node->ec++;
pr_info("---erase bad rsv block:%llx\n",
offset);
}
/* free_node = kzalloc(sizeof(struct free_node_t), */
/* GFP_KERNEL); */
free_node = get_free_node(rsv_info);
if (!free_node)
return -ENOMEM;
free_node->phy_blk_addr =
rsv_info->valid_node->phy_blk_addr;
free_node->ec = rsv_info->valid_node->ec;
temp_node = rsv_info->free_node;
while (temp_node->next)
temp_node = temp_node->next;
temp_node->next = free_node;
temp_node = rsv_info->free_node;
rsv_info->valid_node->phy_blk_addr =
temp_node->phy_blk_addr;
rsv_info->valid_node->phy_page_addr = 0;
rsv_info->valid_node->ec = temp_node->ec;
rsv_info->valid_node->timestamp += 1;
rsv_info->free_node = temp_node->next;
release_free_node(rsv_info, temp_node);
}
} else {
temp_node = rsv_info->free_node;
rsv_info->valid_node->phy_blk_addr = temp_node->phy_blk_addr;
rsv_info->valid_node->phy_page_addr = 0;
rsv_info->valid_node->ec = temp_node->ec;
rsv_info->valid_node->timestamp += 1;
rsv_info->free_node = temp_node->next;
release_free_node(rsv_info, temp_node);
}
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += rsv_info->valid_node->phy_page_addr * mtd->writesize;
if (rsv_info->valid_node->phy_page_addr == 0) {
ret = mtd->_block_isbad(mtd, offset);
if (ret) {
/*
*bad block here, need fix it
*because of info_blk list may be include bad block,
*so we need check it and done here. if don't,
*some bad blocks may be erase here
*and env will lost or too much ecc error
**/
pr_info("have bad block in info_blk list!!!!\n");
rsv_info->valid_node->phy_page_addr =
pages_per_blk - i;
goto RE_SEARCH;
}
memset(&erase_info, 0, sizeof(struct erase_info));
erase_info.addr = offset;
erase_info.len = mtd->erasesize;
_aml_rsv_disprotect();
ret = mtd->_erase(mtd, &erase_info);
_aml_rsv_protect();
if (ret) {
pr_info("env free blk erase failed %d\n", ret);
mtd->_block_markbad(mtd, offset);
return ret;
}
rsv_info->valid_node->ec++;
}
ret = meson_rsv_write(rsv_info, buf);
if (ret) {
pr_info("update nand rsv FAILED!\n");
return 1;
}
rsv_info->valid = 1;
/* clear status when write successfully*/
rsv_info->valid_node->status = 0;
return ret;
}
int meson_rsv_scan(struct meson_rsv_info_t *rsv_info)
{
struct mtd_info *mtd = rsv_info->mtd;
struct mtd_oob_ops oob_ops;
struct oobinfo_t oobinfo;
struct free_node_t *free_node, *temp_node;
u64 offset;
u32 start, end;
int ret = 0, error, rsv_status, i, k;
u8 scan_status;
u8 good_addr[256] = {0};
u32 page_num, pages_per_blk;
u32 remainder;
RE_RSV_INFO_EXT:
start = rsv_info->start_block;
end = rsv_info->end_block;
pr_info("%s: info size=0x%x max_scan_blk=%d, start_blk=%d\n",
rsv_info->name, rsv_info->size,
end, start);
do {
offset = mtd->erasesize;
offset *= start;
scan_status = 0;
RE_RSV_INFO:
oob_ops.mode = MTD_OPS_AUTO_OOB;
oob_ops.len = 0;
oob_ops.ooblen = sizeof(struct oobinfo_t);
oob_ops.ooboffs = 0;
oob_ops.datbuf = NULL;
oob_ops.oobbuf = (u8 *)&oobinfo;
memset((u8 *)&oobinfo, 0, sizeof(struct oobinfo_t));
error = mtd->_read_oob(mtd, offset, &oob_ops);
if (error != 0 && error != -EUCLEAN) {
pr_info("blk check good but read failed: %llx, %d\n",
(u64)offset, error);
offset += rsv_info->size;
div_u64_rem(offset, mtd->erasesize, &remainder);
if (scan_status++ > 6 || !remainder) {
pr_info("ECC error, scan ONE block exit\n");
scan_status = 0;
continue;
}
goto RE_RSV_INFO;
}
rsv_info->init = 1;
rsv_info->valid_node->status = 0;
if (!memcmp(oobinfo.name, rsv_info->name, 4)) {
rsv_info->valid = 1;
if (rsv_info->valid_node->phy_blk_addr >= 0) {
free_node = get_free_node(rsv_info);
if (!free_node)
return -ENOMEM;
free_node->dirty_flag = 1;
if (oobinfo.timestamp > rsv_info->valid_node->timestamp) {
free_node->phy_blk_addr =
rsv_info->valid_node->phy_blk_addr;
free_node->ec =
rsv_info->valid_node->ec;
rsv_info->valid_node->phy_blk_addr =
start;
rsv_info->valid_node->phy_page_addr = 0;
rsv_info->valid_node->ec = oobinfo.ec;
rsv_info->valid_node->timestamp =
oobinfo.timestamp;
} else {
free_node->phy_blk_addr = start;
free_node->ec = oobinfo.ec;
}
if (!rsv_info->free_node) {
rsv_info->free_node = free_node;
} else {
temp_node = rsv_info->free_node;
while (temp_node->next)
temp_node = temp_node->next;
temp_node->next = free_node;
}
} else {
rsv_info->valid_node->phy_blk_addr =
start;
rsv_info->valid_node->phy_page_addr = 0;
rsv_info->valid_node->ec = oobinfo.ec;
rsv_info->valid_node->timestamp =
oobinfo.timestamp;
}
} else {
free_node = get_free_node(rsv_info);
if (!free_node)
return -ENOMEM;
free_node->phy_blk_addr = start;
free_node->ec = oobinfo.ec;
if (!rsv_info->free_node) {
rsv_info->free_node = free_node;
} else {
temp_node = rsv_info->free_node;
while (temp_node->next)
temp_node = temp_node->next;
temp_node->next = free_node;
}
}
} while ((++start) < end);
pr_info("%s : phy_blk_addr=%d, ec=%d, phy_page_addr=%d, timestamp=%d\n",
rsv_info->name,
rsv_info->valid_node->phy_blk_addr,
rsv_info->valid_node->ec,
rsv_info->valid_node->phy_page_addr,
rsv_info->valid_node->timestamp);
pr_info("%s free list:\n", rsv_info->name);
temp_node = rsv_info->free_node;
while (temp_node) {
pr_info("blockN=%d, ec=%d, dirty_flag=%d\n",
temp_node->phy_blk_addr,
temp_node->ec,
temp_node->dirty_flag);
temp_node = temp_node->next;
}
/*second stage*/
pages_per_blk = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
page_num = rsv_info->size >> mtd->writesize_shift;
if (page_num == 0)
page_num++;
pr_info("%s %d: page_num=%d\n", __func__, __LINE__, page_num);
if (rsv_info->valid == 1) {
oob_ops.mode = MTD_OPS_AUTO_OOB;
oob_ops.len = 0;
oob_ops.ooblen = sizeof(struct oobinfo_t);
oob_ops.ooboffs = 0;
oob_ops.datbuf = NULL;
oob_ops.oobbuf = (u8 *)&oobinfo;
for (i = 0; i < pages_per_blk; i++) {
memset((u8 *)&oobinfo, 0, oob_ops.ooblen);
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += i * mtd->writesize;
error = mtd->_read_oob(mtd, offset, &oob_ops);
if (error != 0 && error != -EUCLEAN) {
pr_info("blk good but read failed:%llx,%d\n",
(u64)offset, error);
rsv_info->valid_node->status |= ECC_ABNORMAL_FLAG;
ret = -1;
continue;
}
if (!memcmp(oobinfo.name, rsv_info->name, 4)) {
good_addr[i] = 1;
rsv_info->valid_node->phy_page_addr = i;
} else {
break;
}
}
}
if (mtd->writesize < rsv_info->size &&
rsv_info->valid == 1) {
i = rsv_info->valid_node->phy_page_addr;
if (((i + 1) % page_num) != 0) {
ret = -1;
rsv_info->valid_node->status |= POWER_ABNORMAL_FLAG;
pr_info("find %s incomplete\n", rsv_info->name);
}
if (ret == -1) {
for (i = 0; i < (pages_per_blk / page_num); i++) {
rsv_status = 0;
for (k = 0; k < page_num; k++) {
if (!good_addr[k + i * page_num]) {
rsv_status = 1;
break;
}
}
if (!rsv_status) {
pr_info("find %d page ok\n",
i * page_num);
rsv_info->valid_node->phy_page_addr =
k + i * page_num - 1;
ret = 0;
}
}
}
if (ret == -1) {
rsv_info->valid_node->status = 0;
meson_free_rsv_info(rsv_info);
goto RE_RSV_INFO_EXT;
}
i = (rsv_info->size + mtd->writesize - 1) / mtd->writesize;
rsv_info->valid_node->phy_page_addr -= (i - 1);
}
if (rsv_info->valid != 1)
ret = -1;
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += rsv_info->valid_node->phy_page_addr * mtd->writesize;
pr_info("%s valid addr: %llx\n", rsv_info->name, (u64)offset);
return ret;
}
EXPORT_SYMBOL(meson_rsv_scan);
int meson_rsv_read(struct meson_rsv_info_t *rsv_info, u_char *buf)
{
struct mtd_info *mtd = rsv_info->mtd;
struct oobinfo_t oobinfo;
struct mtd_oob_ops oob_ops;
size_t length = 0;
loff_t offset;
int ret = 0;
READ_RSV_AGAIN:
offset = rsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += rsv_info->valid_node->phy_page_addr * mtd->writesize;
pr_info("%s:%d,read info %s from %llx\n", __func__, __LINE__,
rsv_info->name, offset);
while (length < rsv_info->size) {
oob_ops.mode = MTD_OPS_AUTO_OOB;
oob_ops.len = min_t(u32, mtd->writesize,
(rsv_info->size - length));
oob_ops.ooblen = sizeof(struct oobinfo_t);
oob_ops.ooboffs = 0;
oob_ops.datbuf = buf + length;
oob_ops.oobbuf = (u8 *)&oobinfo;
memset((u8 *)&oobinfo, 0, oob_ops.ooblen);
ret = mtd->_read_oob(mtd, offset, &oob_ops);
if (ret != 0 && ret != -EUCLEAN) {
pr_info("blk good but read failed: %llx, %d\n",
(u64)offset, ret);
ret = meson_rsv_scan(rsv_info);
if (ret == -1)
return 1;
goto READ_RSV_AGAIN;
}
if (memcmp(oobinfo.name, rsv_info->name, 4))
pr_info("invalid nand info %s magic: %llx\n",
rsv_info->name, (u64)offset);
offset += mtd->writesize;
length += oob_ops.len;
}
return ret;
}
EXPORT_SYMBOL(meson_rsv_read);
int meson_rsv_check(struct meson_rsv_info_t *rsv_info)
{
int ret = 0;
ret = meson_rsv_scan(rsv_info);
if (ret)
pr_info("%s %d %s info check failed ret %d\n",
__func__, __LINE__, rsv_info->name, ret);
if (!rsv_info->valid) {
pr_info("%s %d no %s info exist\n",
__func__, __LINE__, rsv_info->name);
ret = 1;
}
return ret;
}
EXPORT_SYMBOL(meson_rsv_check);
int meson_rsv_bbt_read(u_char *dest, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->bbt) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!dest || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, dest, size);
return 1;
}
len = rsv_handler->bbt->size;
temp = kzalloc(len, GFP_KERNEL);
ret = meson_rsv_read(rsv_handler->bbt, temp);
memcpy(dest, temp, len > size ? size : len);
pr_info("%s %d read 0x%zx bytes from bbt, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_key_read(u_char *dest, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->key) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!dest || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, dest, size);
return 1;
}
len = rsv_handler->key->size;
temp = kzalloc(len, GFP_KERNEL);
ret = meson_rsv_read(rsv_handler->key, temp);
memcpy(dest, temp, len > size ? size : len);
pr_info("%s %d read 0x%zx bytes from key, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_env_read(u_char *dest, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->env) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!dest || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, dest, size);
return 1;
}
len = rsv_handler->env->size;
temp = kzalloc(len, GFP_KERNEL);
ret = meson_rsv_read(rsv_handler->env, temp);
memcpy(dest, temp, len > size ? size : len);
pr_info("%s %d read 0x%zx bytes from env, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_dtb_read(u_char *dest, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->dtb) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!dest || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, dest, size);
return 1;
}
len = rsv_handler->dtb->size;
temp = kzalloc(len, GFP_KERNEL);
ret = meson_rsv_read(rsv_handler->dtb, temp);
memcpy(dest, temp, len > size ? size : len);
pr_info("%s %d read 0x%zx bytes from dtb, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_bbt_write(u_char *source, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->bbt) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!source || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, source, size);
return 1;
}
len = rsv_handler->bbt->size;
temp = kzalloc(len, GFP_KERNEL);
memcpy(temp, source, len > size ? size : len);
ret = meson_rsv_save(rsv_handler->bbt, temp);
pr_info("%s %d write 0x%zx bytes to bbt, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
EXPORT_SYMBOL(meson_rsv_bbt_write);
int meson_rsv_key_write(u_char *source, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->key) {
pr_info("%s %d rsv info not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!source || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, source, size);
return 1;
}
len = rsv_handler->key->size;
temp = kzalloc(len, GFP_KERNEL);
memcpy(temp, source, len > size ? size : len);
pr_debug("%s %d write size 0x%zx bytes,want len: 0x%zx to key\n",
__func__, __LINE__, len, size);
ret = meson_rsv_save(rsv_handler->key, temp);
pr_info("%s %d write key, ret %d\n",
__func__, __LINE__, ret);
kfree(temp);
return ret;
}
int meson_rsv_env_write(u_char *source, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->env) {
pr_info("%s %d rsv info has not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!source || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, source, size);
return 1;
}
len = rsv_handler->env->size;
temp = kzalloc(len, GFP_KERNEL);
memcpy(temp, source, len > size ? size : len);
ret = meson_rsv_save(rsv_handler->env, temp);
pr_info("%s %d write 0x%zx bytes to env, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_dtb_write(u_char *source, size_t size)
{
u_char *temp;
size_t len;
int ret;
if (!rsv_handler ||
!rsv_handler->dtb) {
pr_info("%s %d rsv info has not inited yet!\n",
__func__, __LINE__);
return 1;
}
if (!source || size == 0) {
pr_info("%s %d parameter error %p %zd\n",
__func__, __LINE__, source, size);
return 1;
}
len = rsv_handler->dtb->size;
temp = kzalloc(len, GFP_KERNEL);
memcpy(temp, source, len > size ? size : len);
ret = meson_rsv_save(rsv_handler->dtb, temp);
pr_info("%s %d write 0x%zx bytes to dtb, ret %d\n",
__func__, __LINE__, len > size ? size : len, ret);
kfree(temp);
return ret;
}
int meson_rsv_init(struct mtd_info *mtd,
struct meson_rsv_handler_t *handler)
{
int i, ret = 0;
u32 pages_per_blk_shift, start, vernier;
pages_per_blk_shift = mtd->erasesize_shift - mtd->writesize_shift;
start = BOOT_TOTAL_PAGES >> pages_per_blk_shift;
start += NAND_GAP_BLOCK_NUM;
vernier = start;
handler->freeNodeBitmask = 0;
for (i = 0; i < NAND_RSV_BLOCK_NUM; i++) {
handler->free_node[i] =
kzalloc(sizeof(struct free_node_t), GFP_KERNEL);
if (!handler->free_node[i]) {
ret = -ENOMEM;
goto error;
}
handler->free_node[i]->index = i;
}
/*bbt info init*/
handler->bbt =
kzalloc(sizeof(*handler->bbt), GFP_KERNEL);
if (!handler->bbt) {
ret = -ENOMEM;
goto error;
}
handler->bbt->valid_node =
kzalloc(sizeof(*handler->bbt->valid_node), GFP_KERNEL);
if (!handler->bbt->valid_node) {
ret = -ENOMEM;
goto error;
}
handler->bbt->mtd = mtd;
handler->bbt->start_block = vernier;
handler->bbt->end_block =
vernier + NAND_BBT_BLOCK_NUM;
handler->bbt->valid_node->phy_blk_addr = -1;
handler->bbt->size = mtd->size >> mtd->erasesize_shift;
handler->bbt->handler = handler;
handler->bbt->read = meson_rsv_bbt_read;
handler->bbt->write = meson_rsv_bbt_write;
memcpy(handler->bbt->name, BBT_NAND_MAGIC, 4);
vernier += NAND_BBT_BLOCK_NUM;
#ifndef CONFIG_MTD_ENV_IN_NAND
/*env info init*/
handler->env =
kzalloc(sizeof(*handler->env), GFP_KERNEL);
if (!handler->env) {
ret = -ENOMEM;
goto error;
}
handler->env->valid_node =
kzalloc(sizeof(struct valid_node_t), GFP_KERNEL);
if (!handler->env->valid_node) {
ret = -ENOMEM;
goto error;
}
handler->env->mtd = mtd;
handler->env->start_block = vernier;
handler->env->end_block =
vernier + NAND_ENV_BLOCK_NUM;
handler->env->valid_node->phy_blk_addr = -1;
handler->env->size = CONFIG_ENV_SIZE;
handler->env->handler = handler;
handler->env->read = meson_rsv_env_read;
handler->env->write = meson_rsv_env_write;
memcpy(handler->env->name, ENV_NAND_MAGIC, 4);
vernier += NAND_ENV_BLOCK_NUM;
#endif
handler->key =
kzalloc(sizeof(*handler->key), GFP_KERNEL);
if (!handler->key) {
ret = -ENOMEM;
goto error;
}
/*key init*/
handler->key->valid_node =
kzalloc(sizeof(struct valid_node_t), GFP_KERNEL);
if (!handler->key->valid_node) {
ret = -ENOMEM;
goto error;
}
handler->key->mtd = mtd;
handler->key->start_block = vernier;
handler->key->end_block =
vernier + NAND_KEY_BLOCK_NUM;
handler->key->valid_node->phy_blk_addr = -1;
handler->key->size = 0;
handler->key->handler = handler;
handler->key->read = meson_rsv_key_read;
handler->key->write = meson_rsv_key_write;
memcpy(handler->key->name, KEY_NAND_MAGIC, 4);
vernier += NAND_KEY_BLOCK_NUM;
handler->dtb =
kzalloc(sizeof(*handler->dtb), GFP_KERNEL);
if (!handler->dtb) {
ret = -ENOMEM;
goto error;
}
handler->dtb->valid_node =
kzalloc(sizeof(struct valid_node_t), GFP_KERNEL);
if (!handler->dtb->valid_node) {
ret = -ENOMEM;
goto error;
}
handler->dtb->mtd = mtd;
handler->dtb->start_block = vernier;
handler->dtb->end_block =
vernier + NAND_DTB_BLOCK_NUM;
handler->dtb->valid_node->phy_blk_addr = -1;
handler->dtb->size = 0;
handler->dtb->handler = handler;
handler->dtb->read = meson_rsv_dtb_read;
handler->dtb->write = meson_rsv_dtb_write;
memcpy(handler->dtb->name, DTB_NAND_MAGIC, 4);
vernier += NAND_DTB_BLOCK_NUM;
if (mtd->erasesize < 0x40000) {
handler->key->size = mtd->erasesize >> 2;
handler->dtb->size = mtd->erasesize >> 1;
} else {
handler->key->size = 0x40000;
handler->dtb->size = 0x40000;
}
if ((vernier - start) > NAND_RSV_BLOCK_NUM) {
pr_info("ERROR: total blk number is over the limit\n");
ret = -ENOMEM;
goto error;
}
meson_rsv_register_cdev(handler->dtb, DTB_CDEV_NAME);
#ifndef CONFIG_MTD_ENV_IN_NAND
meson_rsv_register_cdev(handler->env, ENV_CDEV_NAME);
#endif
meson_rsv_register_unifykey(handler->key);
rsv_handler = handler;
pr_info("bbt_start=%d\n", handler->bbt->start_block);
#ifndef CONFIG_MTD_ENV_IN_NAND
pr_info("env_start=%d\n", handler->env->start_block);
#endif
pr_info("key_start=%d\n", handler->key->start_block);
pr_info("dtb_start=%d\n", handler->dtb->start_block);
return ret;
error:
for (i = 0; i < NAND_RSV_BLOCK_NUM; i++) {
kfree(handler->free_node[i]);
handler->free_node[i] = NULL;
}
kfree(handler->bbt->valid_node);
kfree(handler->bbt);
handler->bbt = NULL;
kfree(handler->env->valid_node);
kfree(handler->env);
handler->env = NULL;
kfree(handler->key->valid_node);
kfree(handler->key);
handler->key = NULL;
kfree(handler->dtb->valid_node);
kfree(handler->dtb);
handler->dtb = NULL;
return ret;
}
EXPORT_SYMBOL(meson_rsv_init);