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)
715 lines
16 KiB
C
715 lines
16 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_bmt.h"
|
|
#include "nandx_pmt.h"
|
|
#include "nandx_core.h"
|
|
#include "nandx_platform.h"
|
|
#include "nandx_ops.h"
|
|
|
|
/*
|
|
* this file is just for pl & lk & mtd,
|
|
* mntl wrapper is independent
|
|
*/
|
|
|
|
struct ops_data {
|
|
long long base;
|
|
size_t len;
|
|
u8 *data;
|
|
u8 *oob;
|
|
};
|
|
|
|
struct wrap_ops {
|
|
long long offs;
|
|
size_t len;
|
|
u8 *data;
|
|
u8 *oob;
|
|
};
|
|
|
|
static struct nandx_lock nlock;
|
|
|
|
static int set_ops_table(struct nandx_chip_info *info,
|
|
struct nandx_ops *ops_table, int count,
|
|
struct ops_data *odata)
|
|
{
|
|
u32 start_row;
|
|
u32 page_size;
|
|
size_t tmp_len, tmp_count;
|
|
long long page_align_start;
|
|
struct nandx_ops *ops, *tmp_ops;
|
|
|
|
tmp_len = odata->len;
|
|
ops = ops_table;
|
|
tmp_ops = ops;
|
|
|
|
page_size = info->page_size;
|
|
|
|
page_align_start = div_round_down(odata->base, page_size);
|
|
start_row = (u32)div_down(page_align_start, page_size);
|
|
tmp_count = count;
|
|
while (tmp_len) {
|
|
ops->row = start_row;
|
|
if (ops == ops_table) {
|
|
ops->col = odata->base - page_align_start;
|
|
ops->len = MIN(page_size - ops->col, tmp_len);
|
|
ops->data = odata->data;
|
|
ops->oob = odata->oob;
|
|
} else {
|
|
ops->col = 0;
|
|
ops->len = MIN(page_size, tmp_len);
|
|
ops->data = tmp_ops->data + tmp_ops->len;
|
|
/* only one oob data */
|
|
ops->oob = odata->oob;
|
|
}
|
|
|
|
tmp_ops = ops;
|
|
tmp_len -= ops->len;
|
|
start_row++;
|
|
ops++;
|
|
tmp_count--;
|
|
}
|
|
|
|
NANDX_ASSERT(tmp_count == 0);
|
|
return 0;
|
|
}
|
|
|
|
static struct nandx_ops *get_error_ops(struct nandx_ops *ops_table, int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (ops_table[i].status < 0 &&
|
|
ops_table[i].status != -ENANDFLIPS)
|
|
return &ops_table[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int nandx_ops_block_rw(struct nandx_core *ncore,
|
|
struct wrap_ops *wrapops, bool is_raw,
|
|
bool do_multi, void *callback,
|
|
struct nandx_ops *err_ops)
|
|
{
|
|
size_t new_len;
|
|
int ret, ops_count, plane_num, mode = 0;
|
|
long long page_align_start, end;
|
|
u32 page_size;
|
|
struct ops_data odata;
|
|
struct nandx_ops *ops, *ops_table;
|
|
struct nandx_chip_info *info;
|
|
nandx_core_rw_cb nandx_core_rw;
|
|
|
|
info = ncore->info;
|
|
plane_num = info->plane_num;
|
|
if (plane_num > 1 && do_multi)
|
|
mode |= MODE_MULTI;
|
|
|
|
if (!do_multi)
|
|
plane_num = 1;
|
|
|
|
if (is_raw)
|
|
mode |= MODE_SLC;
|
|
|
|
page_size = info->page_size;
|
|
|
|
end = wrapops->offs + wrapops->len - 1;
|
|
page_align_start = div_round_down(wrapops->offs, page_size);
|
|
new_len = end - page_align_start + 1;
|
|
|
|
ops_count = div_up(new_len, page_size);
|
|
if (ops_count % plane_num) {
|
|
pr_err("%s: count %d not plane align\n", __func__, ops_count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ops_table = alloc_ops_table(ops_count);
|
|
if (!ops_table) {
|
|
NANDX_ASSERT(ops_table);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
odata.base = wrapops->offs;
|
|
odata.len = wrapops->len;
|
|
odata.data = wrapops->data;
|
|
odata.oob = wrapops->oob;
|
|
ret = set_ops_table(info, ops_table, ops_count, &odata);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
if (ops_count / plane_num > 1)
|
|
mode |= MODE_CACHE;
|
|
|
|
nandx_core_rw = (nandx_core_rw_cb)callback;
|
|
ret = nandx_core_rw(ops_table, ops_count, mode);
|
|
if (ret < 0 && ret != -ENANDFLIPS) {
|
|
if (err_ops) {
|
|
ops = get_error_ops(ops_table, ops_count);
|
|
NANDX_ASSERT(ops);
|
|
if (ops)
|
|
memcpy(err_ops, ops,
|
|
sizeof(struct nandx_ops));
|
|
}
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
free_ops_table(ops_table);
|
|
return ret;
|
|
}
|
|
|
|
static int nandx_write_error_handler(struct nandx_core *ncore,
|
|
u32 err_blk, u32 err_page,
|
|
u8 *buf, bool do_multi)
|
|
{
|
|
u8 *data;
|
|
bool raw_part;
|
|
size_t size;
|
|
long long err_addr, new_addr;
|
|
u32 new_block, oob_size, page_size, block_size;
|
|
int ret, plane_num;
|
|
struct wrap_ops wrap;
|
|
struct pt_resident *pt;
|
|
|
|
page_size = ncore->info->page_size;
|
|
block_size = ncore->info->block_size;
|
|
oob_size = ncore->info->oob_size;
|
|
err_addr = err_blk * block_size;
|
|
pt = nandx_pmt_get_partition(err_addr);
|
|
raw_part = nandx_pmt_is_raw_partition(pt);
|
|
|
|
plane_num = ncore->info->plane_num;
|
|
if (!do_multi)
|
|
plane_num = 1;
|
|
|
|
if (raw_part) {
|
|
data = mem_alloc(err_page, page_size);
|
|
if (!data) {
|
|
NANDX_ASSERT(buf);
|
|
return -ENOMEM;
|
|
}
|
|
size = err_page * page_size;
|
|
} else {
|
|
data = buf;
|
|
size = block_size * plane_num;
|
|
}
|
|
|
|
wrap.offs = err_addr;
|
|
wrap.len = size;
|
|
wrap.data = data;
|
|
wrap.oob = mem_alloc(1, oob_size);
|
|
if (!wrap.oob) {
|
|
ret = -ENOMEM;
|
|
goto read_err;
|
|
}
|
|
|
|
nandx_bmt_update_oob(err_blk, wrap.oob);
|
|
ret = nandx_ops_block_rw(ncore, &wrap, raw_part,
|
|
do_multi, nandx_core_read, NULL);
|
|
if (ret < 0)
|
|
goto oob_err;
|
|
new_block = nandx_ops_mark_bad(err_blk, UPDATE_WRITE_FAIL);
|
|
new_addr = new_block * block_size;
|
|
wrap.offs = new_addr;
|
|
ret = nandx_ops_block_rw(ncore, &wrap, raw_part,
|
|
do_multi, nandx_core_write, NULL);
|
|
|
|
oob_err:
|
|
mem_free(wrap.oob);
|
|
read_err:
|
|
if (raw_part)
|
|
mem_free(data);
|
|
return ret;
|
|
}
|
|
|
|
static int nandx_ops_rw(struct nandx_core *ncore,
|
|
struct wrap_ops *wrapops,
|
|
bool do_multi, void *callback)
|
|
{
|
|
u8 *data;
|
|
bool raw_part;
|
|
size_t size, left;
|
|
int ret, block_page_num;
|
|
long long addr, map_addr;
|
|
u32 page_size, oob_size, erase_size;
|
|
u32 err_page, block, map_block;
|
|
struct nandx_ops err_ops;
|
|
struct wrap_ops wrap;
|
|
struct pt_resident *pt;
|
|
struct nandx_chip_info *info;
|
|
|
|
info = ncore->info;
|
|
page_size = info->page_size;
|
|
oob_size = info->oob_size;
|
|
erase_size = info->block_size;
|
|
block_page_num = info->block_size / page_size;
|
|
|
|
addr = wrapops->offs;
|
|
left = wrapops->len;
|
|
data = wrapops->data;
|
|
|
|
pt = nandx_pmt_get_partition(addr);
|
|
raw_part = nandx_pmt_is_raw_partition(pt);
|
|
if (raw_part)
|
|
erase_size /= info->wl_page_num;
|
|
size = erase_size - reminder(wrapops->offs, erase_size);
|
|
size = MIN(wrapops->len, size);
|
|
|
|
wrap.oob = mem_alloc(1, oob_size);
|
|
if (!wrap.oob)
|
|
return -ENOMEM;
|
|
|
|
while (left) {
|
|
/*
|
|
* unmark it if want to support cross partitions r/w.
|
|
* pt = nandx_pmt_get_partition(addr);
|
|
* raw_part = nandx_pmt_is_raw_partition(pt);
|
|
* erase_size = info->block_size;
|
|
* if (raw_part)
|
|
* erase_size = info->block_size / info->wl_page_num;
|
|
*/
|
|
nandx_ops_addr_transfer(ncore, addr, &block, &map_block);
|
|
map_addr = (long long)map_block * info->block_size +
|
|
reminder(addr, erase_size);
|
|
|
|
wrap.offs = map_addr;
|
|
wrap.len = size;
|
|
wrap.data = data;
|
|
nandx_bmt_update_oob(block, wrap.oob);
|
|
|
|
ret = nandx_ops_block_rw(ncore, &wrap, raw_part,
|
|
do_multi, callback, &err_ops);
|
|
if (ret < 0 && ret != -ENANDFLIPS) {
|
|
if (err_ops.status != -ENANDWRITE)
|
|
goto oob_err;
|
|
err_page = err_ops.row % block_page_num;
|
|
ret = nandx_write_error_handler(ncore, map_block,
|
|
err_page, data,
|
|
do_multi);
|
|
if (ret < 0)
|
|
goto oob_err;
|
|
}
|
|
addr += size;
|
|
data += size;
|
|
left -= size;
|
|
size = MIN(left, erase_size);
|
|
}
|
|
|
|
oob_err:
|
|
mem_free(wrap.oob);
|
|
return 0;
|
|
}
|
|
|
|
static int nandx_ops_rw_oob(struct nandx_core *ncore, long long offs, u8 *oob,
|
|
void *callback)
|
|
{
|
|
bool raw_part;
|
|
int ret, mode = 0;
|
|
u32 page_size;
|
|
struct nandx_ops ops;
|
|
struct nandx_chip_info *info;
|
|
nandx_core_rw_cb nandx_core_rw;
|
|
struct pt_resident *pt;
|
|
|
|
info = ncore->info;
|
|
if (reminder(offs, info->page_size)) {
|
|
pr_err("%s: addr %llu not sector align\n", __func__, offs);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pt = nandx_pmt_get_partition(offs);
|
|
raw_part = nandx_pmt_is_raw_partition(pt);
|
|
|
|
if (raw_part)
|
|
mode |= MODE_SLC;
|
|
|
|
page_size = info->page_size;
|
|
|
|
ops.row = (u32)div_down(offs, page_size);
|
|
ops.col = 0;
|
|
ops.len = 0;
|
|
ops.data = NULL;
|
|
ops.oob = oob;
|
|
nandx_core_rw = (nandx_core_rw_cb)callback;
|
|
ret = nandx_core_rw(&ops, 1, mode);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_read(struct nandx_core *ncore, long long from,
|
|
size_t len, u8 *buf, bool do_multi)
|
|
{
|
|
struct wrap_ops wrap;
|
|
int ret;
|
|
|
|
nandx_get_device(FL_READING);
|
|
|
|
wrap.offs = from;
|
|
wrap.len = len;
|
|
wrap.data = buf;
|
|
ret = nandx_ops_rw(ncore, &wrap, do_multi, nandx_core_read);
|
|
|
|
nandx_release_device();
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_write(struct nandx_core *ncore, long long to,
|
|
size_t len, u8 *buf, bool do_multi)
|
|
{
|
|
struct wrap_ops wrap;
|
|
int ret;
|
|
|
|
nandx_get_device(FL_WRITING);
|
|
|
|
wrap.offs = to;
|
|
wrap.len = len;
|
|
wrap.data = buf;
|
|
ret = nandx_ops_rw(ncore, &wrap, do_multi, nandx_core_write);
|
|
|
|
nandx_release_device();
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_read_oob(struct nandx_core *ncore, long long from, u8 *oob)
|
|
{
|
|
int ret;
|
|
|
|
nandx_get_device(FL_READING);
|
|
|
|
ret = nandx_ops_rw_oob(ncore, from, oob, nandx_core_read);
|
|
|
|
nandx_release_device();
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_write_oob(struct nandx_core *ncore, long long to, u8 *oob)
|
|
{
|
|
int ret;
|
|
|
|
nandx_get_device(FL_WRITING);
|
|
|
|
ret = nandx_ops_rw_oob(ncore, to, oob, nandx_core_write);
|
|
|
|
nandx_release_device();
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_erase_block(struct nandx_core *ncore, long long laddr)
|
|
{
|
|
bool raw_part;
|
|
int plane_num, ret = 0;
|
|
u32 block, map_block, mode = 0;
|
|
u32 rows[2], page_size, block_size, block_page_num;
|
|
struct nandx_chip_info *info;
|
|
struct pt_resident *pt;
|
|
|
|
nandx_get_device(FL_ERASING);
|
|
|
|
info = ncore->info;
|
|
page_size = info->page_size;
|
|
block_size = info->block_size;
|
|
block_page_num = block_size / page_size;
|
|
plane_num = info->plane_num;
|
|
|
|
pt = nandx_pmt_get_partition(laddr);
|
|
raw_part = nandx_pmt_is_raw_partition(pt);
|
|
if (raw_part) {
|
|
mode |= MODE_SLC;
|
|
plane_num = 1;
|
|
} else {
|
|
mode |= MODE_MULTI;
|
|
/* Todo: check how to erase non-raw partition */
|
|
NANDX_ASSERT(0);
|
|
}
|
|
|
|
rows[0] = nandx_ops_addr_transfer(ncore, laddr, &block, &map_block);
|
|
|
|
/*
|
|
* it is not correct for multi plane,
|
|
* but we need not to use multi operation now.
|
|
*/
|
|
if (plane_num > 1)
|
|
rows[1] = rows[0] + block_page_num;
|
|
|
|
ret = nandx_core_erase(rows, plane_num, mode);
|
|
if (ret < 0) {
|
|
pr_err("erase block 0x%x failed\n", map_block);
|
|
ret = nandx_ops_mark_bad(map_block, UPDATE_ERASE_FAIL);
|
|
}
|
|
|
|
nandx_release_device();
|
|
return ret;
|
|
}
|
|
|
|
int nandx_ops_erase(struct nandx_core *ncore, long long offs,
|
|
long long limit, size_t size)
|
|
{
|
|
long long cur_offs;
|
|
u32 erase_size, block_cnt;
|
|
struct nandx_chip_info *info;
|
|
struct pt_resident *pt;
|
|
bool raw_part;
|
|
|
|
info = ncore->info;
|
|
erase_size = info->block_size;
|
|
|
|
pt = nandx_pmt_get_partition(offs);
|
|
raw_part = nandx_pmt_is_raw_partition(pt);
|
|
if (raw_part)
|
|
erase_size /= info->wl_page_num;
|
|
|
|
if (reminder(offs, erase_size) || reminder(size, erase_size)) {
|
|
pr_debug("%s: invalid offs 0x%llx 0x%zx\n",
|
|
__func__, offs, size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* calculate block number of this image */
|
|
block_cnt = size / erase_size;
|
|
|
|
/* erase nand block */
|
|
cur_offs = offs;
|
|
while (block_cnt) {
|
|
if (nandx_ops_erase_block(ncore, cur_offs) < 0) {
|
|
pr_warn("erase 0x%llx fail\n", cur_offs);
|
|
nandx_ops_mark_bad((u32)
|
|
div_down(cur_offs, erase_size),
|
|
UPDATE_ERASE_FAIL);
|
|
}
|
|
|
|
cur_offs += erase_size;
|
|
block_cnt--;
|
|
|
|
if (limit && block_cnt && cur_offs >= limit) {
|
|
pr_warn("off 0x%llx exceed 0x%llx\n",
|
|
cur_offs, limit);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nandx_ops_mark_bad(u32 block, int reason)
|
|
{
|
|
return nandx_bmt_update(block, reason);
|
|
}
|
|
|
|
int nandx_ops_isbad(long long offs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u32 nandx_ops_addr_transfer(struct nandx_core *ncore, long long laddr,
|
|
u32 *blk, u32 *map_blk)
|
|
{
|
|
int block_page_num;
|
|
u32 page_size, block_size;
|
|
u32 page_in_block, block;
|
|
|
|
page_size = ncore->info->page_size;
|
|
block_size = ncore->info->block_size;
|
|
block_page_num = block_size / page_size;
|
|
|
|
nandx_pmt_addr_to_row(laddr, &block, &page_in_block);
|
|
*blk = block;
|
|
*map_blk = nandx_bmt_get_mapped_block(block);
|
|
return *map_blk * block_page_num + page_in_block;
|
|
}
|
|
|
|
void dump_nand_info(struct nandx_chip_info *info)
|
|
{
|
|
pr_info("block_num is %u\n", info->block_num);
|
|
pr_info("block_size is %u\n", info->block_size);
|
|
pr_info("page_size is %u\n", info->page_size);
|
|
pr_info("slc_block_size is %u\n", info->slc_block_size);
|
|
pr_info("oob_size is %u\n", info->oob_size);
|
|
pr_info("sector_size is %u\n", info->sector_size);
|
|
pr_info("plane_num is %d\n", info->plane_num);
|
|
pr_info("wl_page_num is %d\n", info->wl_page_num);
|
|
pr_info("ecc_strength is %u\n", info->ecc_strength);
|
|
}
|
|
|
|
u32 nandx_calculate_bmt_num(struct nandx_chip_info *info)
|
|
{
|
|
u32 ratio;
|
|
|
|
if (is_support_mntl())
|
|
ratio = info->block_num > 2000 ? 1 : 2;
|
|
else
|
|
ratio = 6;
|
|
|
|
return (info->block_num / 100 * ratio);
|
|
}
|
|
|
|
u32 nandx_get_chip_block_num(struct nandx_chip_info *info)
|
|
{
|
|
u32 chip_block_num, bmt_block_num;
|
|
|
|
bmt_block_num = nandx_calculate_bmt_num(info);
|
|
chip_block_num = info->block_num - bmt_block_num - PMT_POOL_SIZE;
|
|
|
|
return chip_block_num;
|
|
}
|
|
|
|
struct nfc_resource nandx_resource[] = {
|
|
/* NANDX_MT8127 */
|
|
{NANDX_MT8127, (void *)NFI_BASE, (void *)NFIECC_BASE,
|
|
(void *)0x10005000, 0, 0, NULL},
|
|
/* NANDX_MT8163 */
|
|
{NANDX_MT8163, (void *)NFI_BASE, (void *)NFIECC_BASE,
|
|
(void *)0x10005000, 0, 0, NULL},
|
|
/* NANDX_MT8167 */
|
|
{NANDX_MT8167, (void *)NFI_BASE, (void *)NFIECC_BASE,
|
|
(void *)0x10005000, 0, 0, NULL},
|
|
/* NANDX_NONE */
|
|
{NANDX_NONE, 0, 0, 0, 0, 0, NULL},
|
|
};
|
|
|
|
struct nandx_core *nandx_device_init(u32 mode)
|
|
{
|
|
int ret;
|
|
u32 bmt_block_num, pmt_block_start;
|
|
struct platform_data *pdata;
|
|
struct nandx_core *ncore;
|
|
struct nandx_chip_info *info;
|
|
enum IC_VER ver;
|
|
|
|
pdata = mem_alloc(1, sizeof(struct platform_data));
|
|
if (!pdata)
|
|
return NULL;
|
|
|
|
ver = nandx_get_chip_version();
|
|
pdata->res = nandx_get_nfc_resource(ver);
|
|
|
|
pdata->freq.freq_async = 133000000;
|
|
pdata->freq.sel_2x_idx = -1;
|
|
pdata->freq.sel_ecc_idx = -1;
|
|
|
|
ret = nandx_platform_init(pdata);
|
|
if (ret < 0)
|
|
goto pdata_err;
|
|
|
|
if (randomizer_is_support(pdata->res->ver))
|
|
mode |= MODE_RANDOMIZE;
|
|
|
|
ncore = nandx_core_init(pdata, mode);
|
|
if (!ncore)
|
|
goto platform_exit;
|
|
|
|
info = ncore->info;
|
|
dump_nand_info(info);
|
|
bmt_block_num = nandx_calculate_bmt_num(info);
|
|
|
|
ret = nandx_bmt_init(ncore->info, bmt_block_num, true);
|
|
if (ret < 0) {
|
|
pr_err("Error: init bmt failed\n");
|
|
NANDX_ASSERT(0);
|
|
goto bmt_err;
|
|
}
|
|
|
|
pmt_block_start = ncore->info->block_num - bmt_block_num;
|
|
pmt_block_start -= PMT_POOL_SIZE;
|
|
ret = nandx_pmt_init(ncore->info, pmt_block_start);
|
|
if (ret < 0) {
|
|
pr_err("Error: init pmt failed\n");
|
|
NANDX_ASSERT(0);
|
|
goto pmt_err;
|
|
}
|
|
|
|
return ncore;
|
|
|
|
pmt_err:
|
|
/* bmt_exit(); */
|
|
bmt_err:
|
|
nandx_core_free();
|
|
platform_exit:
|
|
nandx_platform_power_down(pdata);
|
|
pdata_err:
|
|
mem_free(pdata);
|
|
return NULL;
|
|
}
|
|
|
|
void nandx_lock_init(void)
|
|
{
|
|
init_waitqueue_head(&nlock.wq);
|
|
nlock.lock = nand_lock_create();
|
|
nlock.state = FL_READY;
|
|
}
|
|
|
|
struct nandx_lock *get_nandx_lock(void)
|
|
{
|
|
return &nlock;
|
|
}
|
|
|
|
int nandx_get_device(int new_state)
|
|
{
|
|
struct nandx_lock *nlock = get_nandx_lock();
|
|
struct nandx_core *ncore = get_nandx_core();
|
|
void *lock;
|
|
wait_queue_head_t *wq;
|
|
DECLARE_WAITQUEUE(wait, current);
|
|
bool high_speed_en, ecc_clk_en;
|
|
|
|
lock = nlock->lock;
|
|
wq = &nlock->wq;
|
|
|
|
retry:
|
|
nand_lock(lock);
|
|
|
|
if (nlock->state == FL_READY) {
|
|
if (new_state != FL_READY && new_state != FL_PM_SUSPENDED) {
|
|
high_speed_en = ncore->pdata->freq.sel_2x_idx >= 0;
|
|
ecc_clk_en = ncore->pdata->freq.sel_ecc_idx >= 0;
|
|
nandx_platform_enable_clock(ncore->pdata,
|
|
high_speed_en,
|
|
ecc_clk_en);
|
|
}
|
|
|
|
nlock->state = new_state;
|
|
nand_unlock(lock);
|
|
return 0;
|
|
}
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
add_wait_queue(wq, &wait);
|
|
nand_unlock(lock);
|
|
schedule();
|
|
remove_wait_queue(wq, &wait);
|
|
goto retry;
|
|
}
|
|
|
|
void nandx_release_device(void)
|
|
{
|
|
struct nandx_lock *nlock = get_nandx_lock();
|
|
struct nandx_core *ncore = get_nandx_core();
|
|
void *lock;
|
|
wait_queue_head_t *wq;
|
|
bool high_speed_en, ecc_clk_en;
|
|
|
|
lock = nlock->lock;
|
|
wq = &nlock->wq;
|
|
|
|
/* Release the controller and the chip */
|
|
nand_lock(lock);
|
|
high_speed_en = ncore->pdata->freq.sel_2x_idx >= 0;
|
|
ecc_clk_en = ncore->pdata->freq.sel_ecc_idx >= 0;
|
|
if (nlock->state != FL_READY && nlock->state != FL_PM_SUSPENDED)
|
|
nandx_platform_disable_clock(ncore->pdata, high_speed_en,
|
|
ecc_clk_en);
|
|
nlock->state = FL_READY;
|
|
wake_up(wq);
|
|
nand_unlock(lock);
|
|
}
|