275 lines
7.4 KiB
C
275 lines
7.4 KiB
C
/*
|
|
* Copyright (c) 2007-2019 Allwinnertech Co., Ltd.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#include "nand_base.h"
|
|
|
|
struct device *ndfc_dev;
|
|
struct completion spinand_dma_done;
|
|
void *SPIC0_IO_BASE;
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
static int nand_suspend(struct platform_device *plat_dev, pm_message_t state)
|
|
{
|
|
#if 0
|
|
if (NORMAL_STANDBY == standby_type) {
|
|
nand_dbg_err("[NAND] nand_suspend normal\n");
|
|
|
|
NandHwNormalStandby();
|
|
} else if (SUPER_STANDBY == standby_type) {
|
|
nand_dbg_err("[NAND] nand_suspend super\n");
|
|
NandHwSuperStandby();
|
|
}
|
|
|
|
nand_dbg_err("[NAND] nand_suspend ok \n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
static int nand_resume(struct platform_device *plat_dev)
|
|
{
|
|
#if 0
|
|
if (NORMAL_STANDBY == standby_type) {
|
|
nand_dbg_err("[NAND] nand_resume normal\n");
|
|
NandHwNormalResume();
|
|
} else if (SUPER_STANDBY == standby_type) {
|
|
nand_dbg_err("[NAND] nand_resume super\n");
|
|
NandHwSuperResume();
|
|
}
|
|
|
|
nand_dbg_err("[NAND] nand_resume ok \n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
static int nand_probe(struct platform_device *plat_dev)
|
|
{
|
|
int ret = 0;
|
|
int nand_cache_level = 0, nand_capacity_level = 0;
|
|
nand_dbg_inf("nand probe\n");
|
|
ndfc_dev = &plat_dev->dev;
|
|
|
|
SPIC0_IO_BASE = (void *)of_iomap(ndfc_dev->of_node, 0);
|
|
if (!SPIC0_IO_BASE) {
|
|
nand_dbg_err("Failed to map SPIC0 IO space1\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
init_completion(&spinand_dma_done);
|
|
p_nand_info = NandHwInit();
|
|
if (p_nand_info == NULL) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* nand0_cache_level */
|
|
ret = of_property_read_u32(ndfc_dev->of_node, "nand0_cache_level",
|
|
&nand_cache_level);
|
|
if (ret) {
|
|
nand_dbg_err("Failed to get nand0_cache_level in dts\n");
|
|
nand_cache_level = 0;
|
|
} else if (nand_cache_level == 0x55aaaa55) {
|
|
nand_dbg_inf("nand0_cache_level is no used\n");
|
|
nand_cache_level = 0;
|
|
}
|
|
set_cache_level(p_nand_info, nand_cache_level);
|
|
|
|
/* nand0_capacity_level */
|
|
ret = of_property_read_u32(ndfc_dev->of_node, "nand0_capacity_level",
|
|
&nand_capacity_level);
|
|
if (ret) {
|
|
nand_dbg_err("Failed to get nand_capacity_level in dts\n");
|
|
nand_capacity_level = 0;
|
|
} else if (nand_capacity_level == 0x55aaaa55) {
|
|
nand_dbg_inf("nand_capacity_level is no used\n");
|
|
nand_capacity_level = 0;
|
|
}
|
|
set_capacity_level(p_nand_info, nand_capacity_level);
|
|
|
|
ret = nand_info_init(p_nand_info, 0, 20, NULL);
|
|
if (ret != 0) {
|
|
nand_dbg_err("nand_info_init failed\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
init_blklayer();
|
|
|
|
kthread_run(nand_thread, &mytr, "%sd", "nand_rc");
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
static int nand_remove(struct platform_device *plat_dev)
|
|
{
|
|
nand_dbg_inf("nand_remove\n");
|
|
return 0;
|
|
}
|
|
|
|
static void nand_release_dev(struct device *dev)
|
|
{
|
|
nand_dbg_inf("nand_release dev\n");
|
|
return;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
uint32 shutdown_flush_write_cache(void)
|
|
{
|
|
struct _nftl_blk *nftl_blk;
|
|
struct nand_blk_ops *tr = &mytr;
|
|
|
|
nftl_blk = tr->nftl_blk_head.nftl_blk_next;
|
|
|
|
while (nftl_blk != NULL) {
|
|
nand_dbg_err("shutdown_flush_write_cache\n");
|
|
mutex_lock(nftl_blk->blk_lock);
|
|
nftl_blk->flush_write_cache(nftl_blk, 0xffff);
|
|
nftl_blk = nftl_blk->nftl_blk_next;
|
|
/*mutex_unlock(nftl_blk->blk_lock);*/
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
void nand_shutdown(struct platform_device *plat_dev)
|
|
{
|
|
/* struct nand_blk_dev *dev;*/
|
|
/* struct nand_blk_ops *tr = &mytr;*/
|
|
|
|
/* nand_dbg_err("[NAND]shutdown first\n");*/
|
|
/* list_for_each_entry(dev, &tr->devs, list){*/
|
|
/* while(blk_fetch_request(dev->rq) != NULL){*/
|
|
/* nand_dbg_err("nand_shutdown wait dev %d\n",dev->devnum);*/
|
|
/* set_current_state(TASK_INTERRUPTIBLE);*/
|
|
/* schedule_timeout(HZ>>3);*/
|
|
/* }*/
|
|
/* }*/
|
|
/**/
|
|
/* nand_dbg_err("[NAND]shutdown second\n");*/
|
|
/* list_for_each_entry(dev, &tr->devs, list){*/
|
|
/* while(blk_fetch_request(dev->rq) != NULL){*/
|
|
/* nand_dbg_err("nand_shutdown wait dev %d\n",dev->devnum);*/
|
|
/* set_current_state(TASK_INTERRUPTIBLE);*/
|
|
/* schedule_timeout(HZ>>3);*/
|
|
/* }*/
|
|
/* }*/
|
|
|
|
shutdown_flush_write_cache();
|
|
NandHwShutDown();
|
|
nand_dbg_err("[NAND]shutdown end\n");
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
static const struct of_device_id of_nand_id_table[] = {
|
|
{.compatible = "allwinner,sunxi-spinand"},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver spinand_driver = {
|
|
.probe = nand_probe,
|
|
.remove = nand_remove,
|
|
.shutdown = nand_shutdown,
|
|
.suspend = nand_suspend,
|
|
.resume = nand_resume,
|
|
.driver = {
|
|
.name = "sw_nand",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_nand_id_table,
|
|
} };
|
|
|
|
static struct resource flash_resource = {
|
|
.start = 0,
|
|
.end = 1,
|
|
.flags = 0x1,
|
|
};
|
|
|
|
struct platform_device nand_device = {
|
|
.name = "sw_nand",
|
|
.id = 33,
|
|
.resource = &flash_resource,
|
|
.num_resources = 1,
|
|
.dev = {
|
|
.release = nand_release_dev,
|
|
} };
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
int __init nand_init(void)
|
|
{
|
|
return platform_driver_register(&spinand_driver);
|
|
}
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
void __exit nand_exit(void)
|
|
{
|
|
platform_driver_unregister(&spinand_driver);
|
|
exit_blklayer();
|
|
}
|
|
|
|
/*module_init(nand_init);*/
|
|
/*module_exit(nand_exit);*/
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("nand flash groups");
|
|
MODULE_DESCRIPTION("Generic NAND flash driver code");
|