Files
android/drivers/misc/mediatek/nand/nandx/driver/nandx_pmt.c
T
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

388 lines
8.6 KiB
C

/*
* Copyright (C) 2017 MediaTek Inc.
* Licensed under either
* BSD Licence, (see NOTICE for more details)
* GNU General Public License, version 2.0, (see NOTICE for more details)
*/
#include "nandx_util.h"
#include "nandx_errno.h"
#include "nandx_info.h"
#include "nandx_core.h"
#include "nandx_ops.h"
#include "nandx_bmt.h"
#include "nandx_pmt.h"
static struct pmt_handler *handler;
struct pt_resident *nandx_pmt_get_partition(u64 addr)
{
struct pt_resident *pt = handler->pmt;
u32 i;
if (handler->pmt == NULL) {
pr_err("no valid pmt table!!\n");
return NULL;
}
for (i = 0; i < handler->part_num; i++) {
if (pt->offset > addr)
return (struct pt_resident *)(pt - 1);
pt++;
}
return NULL;
}
static int nandx_read_pmt_table(struct nandx_chip_info *info,
struct nandx_ops *ops)
{
int ret;
u8 *buf = ops->data;
u32 temp;
ret = nandx_core_read(ops, 1, MODE_SLC);
if ((ret < 0) && (ret != -ENANDFLIPS))
return ret;
if (!is_valid_pt(buf) && !is_valid_mpt(buf))
return 0;
temp = *((u32 *)buf + 1);
handler->sys_slc_ratio = (temp >> 16) & 0xFF;
handler->usr_slc_ratio = (temp) & 0xFF;
temp = /*PT_SIG_SIZE */ 8;
memcpy(handler->pmt, buf + temp,
PART_MAX_COUNT * sizeof(struct pt_resident));
temp += PART_MAX_COUNT * sizeof(struct pt_resident);
memcpy(handler->block_bitmap, buf + temp,
div_up(info->block_num, 32) * sizeof(u32));
return 1;
}
int nandx_pmt_init(struct nandx_chip_info *info, u32 start_blk)
{
struct pt_resident *pt;
struct nandx_ops ops;
u32 ppb, sppb;
u32 i, temp;
u8 *buf;
u32 *block_bitmap;
bool pmt_found = false;
int ret;
handler = mem_alloc(1, sizeof(struct pmt_handler));
if (handler == NULL)
return -ENOMEM;
pt = mem_alloc(PART_MAX_COUNT, sizeof(struct pt_resident));
if (pt == NULL) {
ret = -ENOMEM;
goto freehandler;
}
memset(pt, 0, PART_MAX_COUNT * sizeof(struct pt_resident));
/* restore pt to do pmt release later */
handler->pmt = pt;
ppb = info->block_size / info->page_size;
sppb = ppb / info->wl_page_num;
temp = div_up(info->block_num, 32);
block_bitmap = mem_alloc(temp, sizeof(u32));
if (block_bitmap == NULL) {
ret = -ENOMEM;
goto freept;
}
memset(block_bitmap, 0xff, temp * sizeof(u32));
handler->block_bitmap = block_bitmap;
buf = mem_alloc(1, info->page_size);
if (buf == NULL) {
ret = -ENOMEM;
goto freeblock;
}
/* setup pmt_handler */
handler->info = info;
handler->start_blk = start_blk;
nandx_get_device(FL_READING);
/* go to find valid pmt */
ops.col = 0;
ops.len = info->page_size;
ops.data = buf;
ops.oob = NULL;
ops.row = nandx_bmt_get_mapped_block(start_blk);
ops.row *= ppb;
for (i = 0; i < sppb; i++) {
ret = nandx_read_pmt_table(info, &ops);
if (ret < 0) {
ops.row++;
continue;
}
if (ret > 0) {
pmt_found = true;
handler->pmt_page = i;
} else if (pmt_found) {
break;
}
ops.row++;
}
if (!pmt_found) {
/* Not found main pmt, try to find mirror pmt */
ops.row = start_blk + 1;
ops.row = nandx_bmt_get_mapped_block(ops.row) * ppb;
ret = nandx_read_pmt_table(info, &ops);
if (ret > 0) {
pmt_found = true;
pr_info("find the latest mpt at page %d\n", ops.row);
/* recovery main pmt from mirror pmt */
ops.row = nandx_bmt_get_mapped_block(start_blk) * ppb;
ret = nandx_core_erase(&ops.row, 1, MODE_SLC);
if (ret == 0) {
ret = nandx_core_write(&ops, 1, MODE_SLC);
if (ret == 0)
handler->pmt_page = 0;
}
}
} else {
pr_info("find the latest pt at page %d\n", ops.row - 1);
}
nandx_release_device();
if (!pmt_found) {
pr_err("failed to find valid pt!\n");
ret = -EIO;
goto freebuf;
}
handler->part_num = 0;
for (i = 0; i < PART_MAX_COUNT; i++) {
handler->part_num++;
if (!strcmp(pt->name, "BMTPOOL"))
break;
pt++;
}
mem_free(buf);
return 0;
freebuf:
mem_free(buf);
freeblock:
mem_free(block_bitmap);
freept:
mem_free(pt);
freehandler:
mem_free(handler);
return ret;
}
void nandx_pmt_exit(void)
{
mem_free(handler->block_bitmap);
mem_free(handler->pmt);
mem_free(handler);
}
int nandx_pmt_update(void)
{
struct nandx_chip_info *info;
struct nandx_ops ops;
u8 *buf;
u32 ppb, sppb, len = 0;
int ret;
info = handler->info;
ppb = info->block_size / info->page_size;
sppb = ppb / info->wl_page_num;
buf = mem_alloc(1, info->page_size);
if (buf == NULL)
return -ENOMEM;
memset(buf, 0, info->page_size);
pr_debug("%s !\n", __func__);
/* reconstruct pmt buffer */
len = 4;
*(u32 *)(buf + len) = handler->usr_slc_ratio;
*(u32 *)(buf + len) |= handler->sys_slc_ratio << 16;
len += 4;
memcpy(buf + len, handler->pmt,
PART_MAX_COUNT * sizeof(struct pt_resident));
len += PART_MAX_COUNT * sizeof(struct pt_resident);
memcpy(buf + len, handler->block_bitmap,
div_up(info->block_num, 32) * sizeof(u32));
/* erase and write together */
nandx_get_device(FL_WRITING);
ops.col = 0;
ops.len = info->page_size;
ops.data = buf;
ops.oob = NULL;
if (handler->pmt_page == sppb - 1) {
ops.row = handler->start_blk + 1;
ops.row = nandx_bmt_get_mapped_block(ops.row) * ppb;
ret = nandx_core_erase(&ops.row, 1, MODE_SLC);
if (ret) {
pr_err("erase mpt failed!\n");
} else {
*(u32 *)buf = MPT_SIG;
ret = nandx_core_write(&ops, 1, MODE_SLC);
if (ret)
pr_err("write mpt fail\n");
}
ops.row = handler->start_blk;
ops.row = nandx_bmt_get_mapped_block(ops.row) * ppb;
ret = nandx_core_erase(&ops.row, 1, MODE_SLC);
if (ret) {
pr_err("erase pt block failed!\n");
goto release_device;
}
handler->pmt_page = 0;
} else {
handler->pmt_page++;
}
*(u32 *)buf = PT_SIG;
ops.row = handler->start_blk;
ops.row = nandx_bmt_get_mapped_block(ops.row) * ppb;
ops.row += handler->pmt_page;
ret = nandx_core_write(&ops, 1, MODE_SLC);
if (ret)
pr_err("write pt failed at page %d!\n", ops.row);
release_device:
nandx_release_device();
mem_free(buf);
return ret;
}
u64 nandx_pmt_get_start_address(struct pt_resident *pt)
{
return pt->offset;
}
bool nandx_pmt_is_raw_partition(struct pt_resident *pt)
{
/* TODO: || (pt->ext.type == REGION_SLC_MODE) */
return pt->ext.type == REGION_LOW_PAGE ? true : false;
}
struct pmt_handler *nandx_get_pmt_handler(void)
{
return handler;
}
/**
* nandx_pmt_addr_to_row - translate logical address to physical row address
* @addr: the logical address
* @blk: physical block number got
* @page: page number in the physical block got
*
* Attention:
* This function is not designed to deal with blocks in PMT and BMT pool.
*/
int nandx_pmt_addr_to_row(u64 addr, u32 *blk, u32 *page)
{
struct nandx_chip_info *info = handler->info;
struct pt_resident *pt;
u32 ppb = info->block_size / info->page_size;
u64 start, len;
u32 total, slc, div;
bool is_slc;
pt = nandx_pmt_get_partition(addr);
if (!pt) {
NANDX_ASSERT(0);
return -EINVAL;
}
if (nandx_pmt_is_raw_partition(pt)) {
is_slc = true;
start = nandx_pmt_get_start_address(pt);
} else {
len = ((struct pt_resident *)(pt + 1))->offset - pt->offset;
total = (u32)div_down(len, info->block_size);
if (!strcmp(pt->name, "ANDROID")) {
slc = total * handler->sys_slc_ratio / 100;
} else {
/* user data partition */
total -= PMT_POOL_SIZE;
slc = total * handler->usr_slc_ratio / 100;
}
slc = div_round_up(slc, info->wl_page_num);
/* slc buffer start address */
start = pt->offset + (u64)info->block_size * (total - slc);
if (addr >= start) {
is_slc = true;
} else {
is_slc = false;
start = nandx_pmt_get_start_address(pt);
}
}
div = is_slc ? info->wl_page_num : 1;
len = addr - start;
*blk = (u32)div_down(start, info->block_size) +
(u32)div_down(len, info->block_size / div);
*page = (u32)div_down(len, info->page_size) % (ppb / div);
return 0;
}
/**
* nandx_pmt_blk_is_slc - check whether the block is a SLC MODE block
* @addr: the check point
*
* Return true if it is raw partition or slc buffer of non raw partition.
*/
/* Todo: this algorithm is wrong, should update it. */
bool nandx_pmt_blk_is_slc(u64 addr)
{
struct nandx_chip_info *info = handler->info;
struct pt_resident *pt;
u32 total, slc;
u64 len;
pt = nandx_pmt_get_partition(addr);
if (!pt) {
NANDX_ASSERT(0);
return false;
}
if (nandx_pmt_is_raw_partition(pt))
return true;
len = ((struct pt_resident *)(pt + 1))->offset - pt->offset;
total = (u32)div_down(len, info->block_size);
if (!strcmp(pt->name, "ANDROID")) {
slc = total * handler->sys_slc_ratio / 100;
} else {
/* user data partition, BMTPOOL is in the behind of user data */
total -= PMT_POOL_SIZE;
slc = total * handler->usr_slc_ratio / 100;
}
slc = div_round_up(slc, info->wl_page_num);
len = pt->offset + (u64)info->block_size * (total - slc);
return (len <= addr) ? true : false;
}