970 lines
23 KiB
C
970 lines
23 KiB
C
/*
|
|
* Aml nftl dev
|
|
*
|
|
* (C) 2012 8
|
|
*/
|
|
|
|
#include "../include/amlnf_dev.h"
|
|
#include "../include/aml_nftl_I.h"
|
|
#include "partition_table.h"
|
|
int boot_device_flag = 0;
|
|
struct aml_nand_device *aml_nand_dev = NULL;
|
|
|
|
int is_phydev_off_adjust(void)
|
|
{
|
|
int ret = 0;
|
|
#ifdef NAND_ADJUST_PART_TABLE
|
|
ret = 1;
|
|
#endif
|
|
|
|
return ret ;
|
|
}
|
|
|
|
int get_adjust_block_num(void)
|
|
{
|
|
int ret = 0;
|
|
#ifdef NAND_ADJUST_PART_TABLE
|
|
ret = ADJUST_BLOCK_NUM;
|
|
#endif
|
|
return ret ;
|
|
}
|
|
|
|
int amlnf_get_logicdev(struct amlnf_logicdev_t *amlnf_logicdev)
|
|
{
|
|
#ifndef AML_NAND_UBOOT
|
|
mutex_lock(&amlnf_logicdev->lock);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int amlnf_free_logicdev(struct amlnf_logicdev_t *amlnf_logicdev)
|
|
{
|
|
#ifndef AML_NAND_UBOOT
|
|
mutex_unlock(&amlnf_logicdev->lock);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
|
|
static struct class_attribute phydev_class_attrs[] = {
|
|
__ATTR(info, S_IRUGO | S_IWUSR, show_nand_info, NULL),
|
|
__ATTR(verify, S_IRUGO | S_IWUSR, NULL, verify_nand_page),
|
|
__ATTR(dump, S_IRUGO | S_IWUSR, NULL, dump_nand_page),
|
|
__ATTR(bbt_table, S_IRUGO | S_IWUSR, NULL, show_bbt_table),
|
|
__ATTR(test_sync_flag, S_IRUGO | S_IWUSR, NULL, change_test_sync_flag),
|
|
__ATTR(page_read, S_IRUGO | S_IWUSR, NULL, nand_page_read),
|
|
__ATTR(page_write, S_IRUGO | S_IWUSR, NULL, nand_page_write),
|
|
__ATTR(version, S_IRUGO | S_IWUSR, show_amlnf_version_info, NULL),
|
|
__ATTR_NULL
|
|
};
|
|
|
|
#if 0
|
|
static struct class_attribute logicdev_class_attrs[] = {
|
|
__ATTR(part, S_IRUGO , show_part_struct, NULL),
|
|
__ATTR(list, S_IRUGO , show_list, NULL),
|
|
__ATTR(gcall, S_IRUGO , do_gc_all, NULL),
|
|
__ATTR(gcone, S_IRUGO , do_gc_one, NULL),
|
|
__ATTR(test, S_IRUGO | S_IWUSR , NULL, do_test),
|
|
__ATTR_NULL
|
|
};
|
|
|
|
static struct class_attribute nfdev_class_attrs[] = {
|
|
__ATTR(debug, S_IRUGO , nfdev_debug, NULL),
|
|
__ATTR_NULL
|
|
};
|
|
#endif
|
|
|
|
|
|
#if 0
|
|
static struct class phydev_class = {
|
|
.name = "amlphydev",
|
|
.owner = THIS_MODULE,
|
|
.suspend = phydev_cls_suspend,
|
|
.resume = phydev_cls_resume,
|
|
};
|
|
#endif
|
|
|
|
int amlnf_pdev_register(struct amlnand_phydev *phydev)
|
|
{
|
|
int ret = 0;
|
|
/* phydev->dev.class = &phydev_class; */
|
|
dev_set_name(&phydev->dev, phydev->name, 0);
|
|
dev_set_drvdata(&phydev->dev, phydev);
|
|
ret = device_register(&phydev->dev);
|
|
if (ret != 0) {
|
|
aml_nand_msg("device register failed for %s", phydev->name);
|
|
aml_nand_free(phydev);
|
|
goto exit_error0;
|
|
}
|
|
|
|
phydev->cls.name =
|
|
aml_nand_malloc(strlen((const char *)phydev->name)+8);
|
|
snprintf((char *)phydev->cls.name,
|
|
(MAX_DEVICE_NAME_LEN+8),
|
|
"%s%s",
|
|
"phy_",
|
|
(char *)(phydev->name));
|
|
phydev->cls.class_attrs = phydev_class_attrs;
|
|
ret = class_register(&phydev->cls);
|
|
if (ret) {
|
|
aml_nand_msg("class register nand_class fail for %s",
|
|
phydev->name);
|
|
goto exit_error1;
|
|
}
|
|
return 0;
|
|
exit_error1:
|
|
aml_nand_free(phydev->cls.name);
|
|
exit_error0:
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int amlnf_blk_open(struct block_device *bdev, fmode_t mode)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void amlnf_blk_release(struct gendisk *disk, fmode_t mode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
static int amlnf_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int amlnf_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
|
u32 cmd, u32 arg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct block_device_operations amlnf_blk_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = amlnf_blk_open,
|
|
.release = amlnf_blk_release,
|
|
.ioctl = amlnf_blk_ioctl,
|
|
.getgeo = amlnf_blk_getgeo,
|
|
};
|
|
|
|
#define blk_queue_plugged(q) test_bit(18, &(q)->queue_flags)
|
|
|
|
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note : for kernel; for boot is in logic\aml_nftl_init.c
|
|
*****************************************************************************/
|
|
int amlnf_logic_init(u32 flag)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
int ret = 0;
|
|
|
|
aml_nand_msg("amlnand_add_nftl:");
|
|
/* amlnand_show_dev_partition(aml_chip); */
|
|
list_for_each_entry(phydev, &nphy_dev_list, list) {
|
|
if (phydev == NULL)
|
|
break;
|
|
if (strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME))) {
|
|
ret = add_ntd_partitions(phydev);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand add nftl failed");
|
|
goto exit_error;
|
|
}
|
|
}
|
|
if (!strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME))) {
|
|
ret = boot_device_register(phydev);
|
|
if (ret < 0) {
|
|
aml_nand_msg("boot device registe failed");
|
|
goto exit_error;
|
|
}
|
|
}
|
|
}
|
|
exit_error:
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t nand_version_get(struct class *class,
|
|
struct class_attribute *attr,
|
|
char *buf)
|
|
{
|
|
sprintf(buf, "%d", DRV_PHY_VERSION);
|
|
return 0;
|
|
}
|
|
|
|
static void show_partition_table(struct partitions *table)
|
|
{
|
|
int i = 0;
|
|
struct partitions *par_table = NULL;
|
|
|
|
aml_nand_msg("show partition table:");
|
|
|
|
for (i = 0; i < MAX_PART_NUM; i++) {
|
|
par_table = &table[i];
|
|
if (par_table->size == -1) {
|
|
aml_nand_msg("part: %d, name: %10s, size: %-4s",
|
|
i,
|
|
par_table->name,
|
|
"end");
|
|
break;
|
|
} else
|
|
aml_nand_msg("part: %d, name : %10s, size : %-4llx",
|
|
i,
|
|
par_table->name,
|
|
par_table->size);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static ssize_t nand_part_table_get(struct class *class,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
struct amlnand_phydev *phydev = NULL;
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
struct nand_config *config = NULL;
|
|
struct dev_para *dev_paramt = NULL;
|
|
struct partitions *part_table = NULL;
|
|
int i = 0, j = 0, k = 0, m = 0, tmp_num = 0;
|
|
int device_num;
|
|
|
|
device_num = (aml_chip->h_cache_dev)? 3 : 2;
|
|
|
|
list_for_each_entry(phydev, &nphy_dev_list, list) {
|
|
if ((phydev != NULL)
|
|
&& (!strncmp((char *)phydev->name,
|
|
NAND_CODE_NAME,
|
|
strlen((const char *)NAND_CODE_NAME))))
|
|
break;
|
|
}
|
|
|
|
aml_chip = (struct amlnand_chip *)phydev->priv;
|
|
config = aml_chip->config_ptr;
|
|
|
|
part_table = aml_nand_malloc(MAX_PART_NUM*sizeof(struct partitions));
|
|
if (!part_table) {
|
|
aml_nand_msg("nand_part_table_get : malloc failed");
|
|
return 0;
|
|
}
|
|
memset(part_table, 0x0, MAX_PART_NUM*sizeof(struct partitions));
|
|
|
|
if (boot_device_flag == 1) {
|
|
i += 1;
|
|
tmp_num = i;
|
|
}
|
|
|
|
for (; i < device_num + 1; i++) {
|
|
dev_paramt = &config->dev_para[i];
|
|
if ((!strncmp((char *)dev_paramt->name,
|
|
NAND_CODE_NAME,
|
|
strlen((const char *)NAND_CODE_NAME)))) {
|
|
for (j = 0; j < dev_paramt->nr_partitions; j++) {
|
|
memcpy(&(part_table[j].name),
|
|
dev_paramt->partitions[j].name,
|
|
MAX_NAND_PART_NAME_LEN);
|
|
part_table[j].size =
|
|
dev_paramt->partitions[j].size;
|
|
part_table[j].offset =
|
|
dev_paramt->partitions[j].offset;
|
|
part_table[j].mask_flags =
|
|
dev_paramt->partitions[j].mask_flags;
|
|
/*
|
|
aml_nand_msg("CODE: partition name %s, size %llx,
|
|
offset %llx maskflag %d",
|
|
part_table[j].name,
|
|
part_table[j].size,
|
|
part_table[j].offset,
|
|
part_table[j].mask_flags);
|
|
*/
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = tmp_num;
|
|
for (; i < device_num + 1; i++) {
|
|
dev_paramt = &config->dev_para[i];
|
|
/*
|
|
aml_nand_msg("cache : dev_paramt name %s ",dev_paramt->name);
|
|
*/
|
|
if ((!strncmp((char *)dev_paramt->name,
|
|
NAND_CACHE_NAME,
|
|
strlen((const char *)NAND_CACHE_NAME)))) {
|
|
k = j++;
|
|
for (j = 0; j < dev_paramt->nr_partitions; k++, j++) {
|
|
memcpy(&(part_table[k].name),
|
|
dev_paramt->partitions[j].name,
|
|
MAX_NAND_PART_NAME_LEN);
|
|
part_table[k].size =
|
|
dev_paramt->partitions[j].size;
|
|
part_table[k].offset =
|
|
dev_paramt->partitions[j].offset;
|
|
part_table[k].mask_flags =
|
|
dev_paramt->partitions[j].mask_flags;
|
|
/*
|
|
aml_nand_msg("CODE: partition name %s,size %llx,
|
|
offset %llx maskflag %d",
|
|
part_table[k].name,
|
|
part_table[k].size,
|
|
part_table[k].offset,
|
|
part_table[k].mask_flags);
|
|
*/
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = tmp_num;
|
|
for (; i < device_num + 1; i++) {
|
|
dev_paramt = &config->dev_para[i];
|
|
/*
|
|
aml_nand_msg("dev_paramt name %s ",dev_paramt->name);
|
|
*/
|
|
if ((!strncmp((char *)dev_paramt->name,
|
|
NAND_DATA_NAME,
|
|
strlen((const char *)NAND_DATA_NAME)))) {
|
|
m = k++;
|
|
for (j = 0; j < dev_paramt->nr_partitions; j++) {
|
|
memcpy(&(part_table[m].name),
|
|
dev_paramt->partitions[j].name,
|
|
MAX_NAND_PART_NAME_LEN);
|
|
part_table[m].size =
|
|
dev_paramt->partitions[j].size;
|
|
part_table[m].offset =
|
|
dev_paramt->partitions[j].offset;
|
|
part_table[m].mask_flags =
|
|
dev_paramt->partitions[j].mask_flags;
|
|
/*
|
|
aml_nand_msg("CODE:partition name %s,size %llx,
|
|
offset %llx maskflag %d",
|
|
part_table[m].name,
|
|
part_table[m].size,
|
|
part_table[m].offset,
|
|
part_table[m].mask_flags);
|
|
*/
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
show_partition_table(part_table);
|
|
memcpy(buf, part_table, MAX_PART_NUM*sizeof(struct partitions));
|
|
|
|
kfree(part_table);
|
|
part_table = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t store_device_flag_get(struct class *class,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
sprintf(buf, "%d", boot_device_flag);
|
|
return 0;
|
|
}
|
|
|
|
static struct class_attribute aml_version =
|
|
__ATTR(version, S_IRUGO, nand_version_get, NULL);
|
|
static struct class_attribute aml_part_table =
|
|
__ATTR(part_table, S_IRUGO, nand_part_table_get, NULL);
|
|
static struct class_attribute aml_store_device =
|
|
__ATTR(store_device, S_IRUGO, store_device_flag_get, NULL);
|
|
#endif /* AML_NAND_UBOOT */
|
|
/*****************************************************************************
|
|
*Name :
|
|
*Description :
|
|
*Parameter :
|
|
*Return :
|
|
*Note :
|
|
*****************************************************************************/
|
|
int amlnf_dev_init(u32 flag)
|
|
{
|
|
#ifndef AML_NAND_UBOOT
|
|
struct amlnand_phydev *phydev = NULL;
|
|
/* struct amlnf_dev* nf_dev = NULL; */
|
|
struct class *aml_store_class = NULL;
|
|
int ret = 0;
|
|
|
|
list_for_each_entry(phydev, &nphy_dev_list, list) {
|
|
if ((phydev != NULL)
|
|
&& (strncmp((char *)phydev->name,
|
|
NAND_BOOT_NAME,
|
|
strlen((const char *)NAND_BOOT_NAME)))) {
|
|
ret = amlnf_pdev_register(phydev);
|
|
if (ret < 0) {
|
|
aml_nand_msg("nand add nftl failed");
|
|
goto exit_error0;
|
|
}
|
|
}
|
|
}
|
|
|
|
aml_store_class = class_create(THIS_MODULE, "aml_store");
|
|
if (IS_ERR(aml_store_class)) {
|
|
aml_nand_msg("amlnf_dev_init : class cread failed");
|
|
ret = -1;
|
|
goto exit_error0;
|
|
}
|
|
|
|
ret = class_create_file(aml_store_class, &aml_version);
|
|
if (ret) {
|
|
aml_nand_msg("amlnf_dev_init : cannot create sysfs file : ");
|
|
goto out_class1;
|
|
}
|
|
ret = class_create_file(aml_store_class, &aml_part_table);
|
|
if (ret) {
|
|
aml_nand_msg("amlnf_dev_init : cannot create sysfs file : ");
|
|
goto out_class2;
|
|
}
|
|
ret = class_create_file(aml_store_class, &aml_store_device);
|
|
if (ret) {
|
|
aml_nand_msg("amlnf_dev_init : cannot create sysfs file : ");
|
|
goto out_class3;
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
#ifndef AML_NAND_UBOOT
|
|
out_class3:
|
|
class_remove_file(aml_store_class, &aml_part_table);
|
|
out_class2:
|
|
class_remove_file(aml_store_class, &aml_version);
|
|
out_class1:
|
|
class_destroy(aml_store_class);
|
|
exit_error0:
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id amlogic_nand_dt_match[] = {
|
|
{ .compatible = "amlogic,aml_nand",
|
|
},
|
|
{},
|
|
};
|
|
|
|
/*
|
|
static inline struct
|
|
aml_nand_device *aml_get_driver_data(struct platform_device *pdev)
|
|
{
|
|
if (pdev->dev.of_node) {
|
|
const struct of_device_id *match;
|
|
match = of_match_node(amlogic_nand_dt_match, pdev->dev.of_node);
|
|
return (struct aml_nand_device *)match->data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int get_nand_platform(struct aml_nand_device *aml_nand_dev,
|
|
struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
int plat_num = 0;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
|
|
if (pdev->dev.of_node) {
|
|
of_node_get(np);
|
|
ret = of_property_read_u32(np, "plat-num", &plat_num);
|
|
if (ret) {
|
|
aml_nand_msg("%s:%d,please config plat-num item\n",
|
|
__func__,
|
|
__LINE__);
|
|
return -1;
|
|
}
|
|
}
|
|
aml_nand_dbg("plat_num %d ", plat_num);
|
|
|
|
return 0;
|
|
}
|
|
*/
|
|
#endif /* CONFIG_OF */
|
|
/***
|
|
*boot_device_flag = 0 ; indicate spi+nand boot
|
|
*boot_device_flag = 1; indicate nand boot
|
|
***/
|
|
#if 1
|
|
int check_dev(void)
|
|
{
|
|
u32 boot_dev;
|
|
u8 ret = 0;
|
|
|
|
boot_dev = amlnf_read_reg32((volatile uint32_t *)P_AO_SEC_GP_CFG0);
|
|
boot_dev = boot_dev&0xf;
|
|
aml_nand_msg("boot_dev: 0x%x",boot_dev);
|
|
|
|
switch (boot_dev) {
|
|
case STORAGE_DEV_NAND:
|
|
case STORAGE_DEV_EMMC:
|
|
ret = boot_dev;
|
|
break;
|
|
|
|
case STORAGE_DEV_SDCARD:
|
|
aml_nand_msg("warning you may need update your uboot!");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
aml_nand_msg("%s return %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
int poc_cfg_prase(void)
|
|
{
|
|
int boot_flag;
|
|
u32 cpu_type, poc_value;
|
|
|
|
poc_value =
|
|
amlnf_read_reg32(aml_nand_dev->platform_data->poc_reg);
|
|
|
|
cpu_type = get_cpu_type();
|
|
if (cpu_type == MESON_CPU_MAJOR_ID_GX) {
|
|
poc_value = (poc_value >> 6) & 0x03;
|
|
} else if (cpu_type >= MESON_CPU_MAJOR_ID_M8)
|
|
poc_value = ((((poc_value >> 0x09) & 0x01) << 2) |
|
|
((poc_value >> 6) & 0x03));
|
|
else
|
|
poc_value = (poc_value & 0x07);
|
|
|
|
if (cpu_type == MESON_CPU_MAJOR_ID_GX) {
|
|
if (poc_value & 0x2)
|
|
boot_flag = SPI_BOOT_FLAG;
|
|
else
|
|
boot_flag = NAND_BOOT_FLAG; //fixme, ...
|
|
|
|
} else if (cpu_type > MESON_CPU_MAJOR_ID_M8) {
|
|
if (poc_value == 0x05)
|
|
boot_flag = SPI_BOOT_FLAG;
|
|
if ((poc_value == 0x03) || (poc_value == 0x01))
|
|
boot_flag = EMMC_BOOT_FLAG;
|
|
} else {
|
|
if ((poc_value == 0x05) || (poc_value == 0x04))
|
|
boot_flag = SPI_BOOT_FLAG;
|
|
if (poc_value == 0x03)
|
|
boot_flag = EMMC_BOOT_FLAG;
|
|
}
|
|
|
|
if ((poc_value == 0x07) || (poc_value == 0x06))
|
|
boot_flag = NAND_BOOT_FLAG;
|
|
if (poc_value == 0x00)
|
|
boot_flag = EMMC_BOOT_FLAG;
|
|
|
|
return boot_flag;
|
|
}
|
|
|
|
|
|
int check_storage_device(void)
|
|
{
|
|
int value = -1, poc_cfg = -1;
|
|
|
|
poc_cfg = poc_cfg_prase();
|
|
value = boot_device_flag;
|
|
|
|
if ((value == 0)
|
|
|| (value == SPI_NAND_FLAG)
|
|
|| (value == NAND_BOOT_FLAG)) {
|
|
if ((value == 0) || (value == -1)) {
|
|
if (poc_cfg == NAND_BOOT_FLAG)
|
|
boot_device_flag = 1;
|
|
else if (poc_cfg == EMMC_BOOT_FLAG)
|
|
boot_device_flag = -1;
|
|
else if (poc_cfg == SPI_BOOT_FLAG)
|
|
boot_device_flag = 0;
|
|
else if (poc_cfg == CARD_BOOT_FLAG)
|
|
boot_device_flag = 1;
|
|
} else if (value == SPI_NAND_FLAG)
|
|
boot_device_flag = 0;
|
|
else
|
|
boot_device_flag = 1;
|
|
} else
|
|
boot_device_flag = -1;
|
|
|
|
boot_device_flag = 1; //fixme, debug code....
|
|
|
|
aml_nand_msg("boot_device_flag : %d", boot_device_flag);
|
|
if ((boot_device_flag == 0) || (boot_device_flag == 1))
|
|
return 0;
|
|
else {
|
|
boot_device_flag = value;
|
|
return -NAND_FAILED;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(check_storage_device);
|
|
#endif
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
static int __init get_storage_device(char *str)
|
|
{
|
|
int value = -1;
|
|
|
|
if (kstrtoul(str, 16, (u32 *)&value))
|
|
return -EINVAL;
|
|
/*value = strtoul(str, NULL, 16);*/
|
|
aml_nand_msg("get storage device: storage %s", str);
|
|
aml_nand_msg("value=%d", value);
|
|
boot_device_flag = value;
|
|
return 0;
|
|
}
|
|
|
|
early_param("storage", get_storage_device);
|
|
|
|
static int amlnf_get_resource(struct platform_device *pdev)
|
|
{
|
|
struct resource *res_mem, *res_irq;
|
|
int size;
|
|
|
|
/*
|
|
getting Nand Platform Data that are set in dts files by of_xxx functions
|
|
*/
|
|
#ifdef CONFIG_OF
|
|
/*
|
|
pdev->dev.platform_data = aml_get_driver_data(pdev);
|
|
aml_nand_msg("===========================================");
|
|
aml_nand_msg("%s:%d,nand device tree ok,dev-name:%s\n",
|
|
__func__,
|
|
__LINE__,
|
|
dev_name(&pdev->dev));
|
|
*/
|
|
#endif
|
|
/*
|
|
ret = get_nand_platform(pdev->dev.platform_data, pdev);
|
|
*/
|
|
/*aml_nand_dev = pdev->dev.platform_data;*/
|
|
aml_nand_dev = kzalloc(sizeof(struct aml_nand_device), GFP_KERNEL);
|
|
if (!aml_nand_dev) {
|
|
aml_nand_msg("aml_nand_dev not exist\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
aml_nand_dev->platform_data =
|
|
kzalloc(sizeof(struct amlnf_platform_data), GFP_KERNEL);
|
|
if (!aml_nand_dev->platform_data) {
|
|
aml_nand_msg("malloc platform data fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*mapping PORT CONFIG register address*/
|
|
aml_nand_dev->platform_data->poc_reg =
|
|
devm_ioremap_nocache(&pdev->dev, POC_CONFIG_REG, sizeof(int));
|
|
if (aml_nand_dev->platform_data->poc_reg == NULL) {
|
|
dev_err(&pdev->dev, "ioremap External Port Config IO fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
aml_nand_msg("poc_reg = 0x%0x\n",
|
|
(u32)aml_nand_dev->platform_data->poc_reg);
|
|
|
|
/*getting nand platform device IORESOURCE_MEM*/
|
|
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res_mem) {
|
|
dev_err(&pdev->dev, "cannot obtain I/O memory region");
|
|
return -ENODEV;
|
|
}
|
|
|
|
size = resource_size(res_mem);
|
|
aml_nand_msg("%s : %0x %0x\n", __func__, res_mem->start , size);
|
|
|
|
/*mapping Nand Flash Controller register address*/
|
|
/*request mem erea*/
|
|
if (!devm_request_mem_region(&pdev->dev,
|
|
res_mem->start,
|
|
size,
|
|
dev_name(&pdev->dev))) {
|
|
dev_err(&pdev->dev, "Memory region busy\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/*mapping IO, nocache*/
|
|
aml_nand_dev->platform_data->nf_reg_base =
|
|
devm_ioremap_nocache(&pdev->dev,
|
|
res_mem->start,
|
|
size);
|
|
if (aml_nand_dev->platform_data->nf_reg_base == NULL) {
|
|
dev_err(&pdev->dev, "ioremap Nand Flash IO fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*mapping external Clock register address of Nand Flash Controller*/
|
|
aml_nand_dev->platform_data->ext_clk_reg =
|
|
devm_ioremap_nocache(&pdev->dev,
|
|
NAND_CLK_CNTL,
|
|
sizeof(int));
|
|
if (aml_nand_dev->platform_data->ext_clk_reg == NULL) {
|
|
dev_err(&pdev->dev, "ioremap External Nand Clock IO fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
if (!res_irq) {
|
|
dev_err(&pdev->dev, "irq res get fail\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
aml_nand_dev->platform_data->irq = res_irq->start;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else /* AML_NAND_UBOOT */
|
|
|
|
/*fixme, yyh*/
|
|
static int amlnf_get_resource(struct platform_device *pdev)
|
|
{
|
|
/*TODO: */
|
|
aml_nand_dev = kzalloc(sizeof(struct aml_nand_device), GFP_KERNEL);
|
|
if (!aml_nand_dev) {
|
|
aml_nand_msg("aml_nand_dev not exist\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
aml_nand_dev->platform_data =
|
|
kzalloc(sizeof(struct amlnf_platform_data), GFP_KERNEL);
|
|
if (!aml_nand_dev->platform_data) {
|
|
aml_nand_msg("malloc platform data fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
aml_nand_dbg("nand io resources:\n");
|
|
aml_nand_dev->platform_data->poc_reg = (volatile uint32_t *)P_ASSIST_POR_CONFIG;
|
|
aml_nand_dbg("poc_reg = %p\n",
|
|
aml_nand_dev->platform_data->poc_reg);
|
|
|
|
aml_nand_dev->platform_data->nf_reg_base = (volatile uint32_t *)NAND_BASE_APB;
|
|
aml_nand_dbg("nf_reg_base = %p\n",
|
|
aml_nand_dev->platform_data->nf_reg_base);
|
|
|
|
aml_nand_dev->platform_data->ext_clk_reg = (volatile uint32_t *)NAND_CLK_CNTL;
|
|
aml_nand_dbg("ext_clk_reg = %p\n",
|
|
aml_nand_dev->platform_data->ext_clk_reg);
|
|
return 0;
|
|
}
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
extern struct list_head nlogic_dev_list;
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
static int _amlnf_init(struct platform_device *pdev, u32 flag)
|
|
{
|
|
int ret = 0;
|
|
INIT_LIST_HEAD (&nphy_dev_list);
|
|
INIT_LIST_HEAD (&nlogic_dev_list);
|
|
INIT_LIST_HEAD (&nf_dev_list);
|
|
|
|
PHY_NAND_LINE
|
|
/*Nand physic init*/
|
|
/*Amlogic Nand Flash Controller init and Nand chip init ......*/
|
|
/*Scan or Build table,fbbt ,bbt, key and so on*/
|
|
/*Creating physical partition, offset and size*/
|
|
ret = amlnf_phy_init(flag, pdev);
|
|
if (ret) {
|
|
aml_nand_msg("nandphy_init failed and ret=0x%x", ret);
|
|
if (ret == -NAND_FAILED) {
|
|
ret = -1; // controller failed
|
|
}else if(ret == -NAND_SHIPPED_BADBLOCK_FAILED){
|
|
ret = NAND_SHIPPED_BADBLOCK_FAILED;
|
|
}
|
|
else if (ret == -NAND_DETECT_DTB_FAILED)
|
|
{
|
|
ret = NAND_DETECT_DTB_FAILED;
|
|
}
|
|
else{
|
|
ret = 1;
|
|
}
|
|
goto exit_error0;
|
|
}
|
|
|
|
PHY_NAND_LINE
|
|
//only read id, quit myself.
|
|
if (flag == NAND_SCAN_ID_INIT)
|
|
goto exit_error0;
|
|
PHY_NAND_LINE
|
|
/*Nand logic init*/
|
|
ret = amlnf_logic_init(flag);
|
|
if (ret < 0) {
|
|
aml_nand_msg("amlnf_add_nftl failed and ret=0x%x", ret);
|
|
goto exit_error0;
|
|
}
|
|
PHY_NAND_LINE
|
|
ret = amlnf_dev_init(flag);
|
|
if (ret < 0) {
|
|
aml_nand_msg("amlnf_add_nftl failed and ret=0x%x", ret);
|
|
goto exit_error0;
|
|
}
|
|
PHY_NAND_LINE
|
|
exit_error0:
|
|
return ret; /* fixme, */
|
|
}
|
|
#ifndef AML_NAND_UBOOT
|
|
static int amlnf_driver_probe(struct platform_device *pdev)
|
|
#else
|
|
int amlnf_init(u32 flag)
|
|
#endif /* AML_NAND_UBOOT */
|
|
{
|
|
int ret = 0;
|
|
#ifndef AML_NAND_UBOOT
|
|
u32 flag = 0;
|
|
#else
|
|
struct platform_device plat_dev;
|
|
struct platform_device *pdev = &plat_dev;
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
PHY_NAND_LINE
|
|
ret = amlnf_get_resource(pdev);
|
|
if (ret < 0) {
|
|
aml_nand_msg("get resource fail!");
|
|
return 0;
|
|
}
|
|
PHY_NAND_LINE
|
|
/*judge if it is nand boot device*/
|
|
ret = check_storage_device();
|
|
if (ret < 0) {
|
|
aml_nand_msg("do not init nand");
|
|
return 0;
|
|
}
|
|
PHY_NAND_LINE
|
|
/*Initializing Nand Flash*/
|
|
ret = _amlnf_init(pdev, flag);
|
|
PHY_NAND_LINE
|
|
return ret;
|
|
}
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
int amlnf_exit(unsigned flag)
|
|
#else
|
|
static int amlnf_exit(struct platform_device *pdev)
|
|
#endif
|
|
{
|
|
extern void amlnf_phy_exit(void);
|
|
extern void amlnf_logic_exit(void);
|
|
amlnf_phy_exit();
|
|
amlnf_logic_exit();
|
|
aml_nand_msg("amlnf_exit : ok");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifndef AML_NAND_UBOOT
|
|
static int amlnf_driver_remove(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void amlnf_driver_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct amlnand_phydev *phy_dev = NULL;
|
|
struct amlnand_chip *aml_chip = NULL;
|
|
|
|
if (check_storage_device() < 0) {
|
|
aml_nand_msg("without nand");
|
|
return;
|
|
}
|
|
|
|
list_for_each_entry(phy_dev, &nphy_dev_list, list) {
|
|
if (phy_dev) {
|
|
aml_chip = (struct amlnand_chip *)phy_dev->priv;
|
|
amlnand_get_device(aml_chip, CHIP_SHUTDOWN);
|
|
phy_dev->option |= NAND_SHUT_DOWN;
|
|
amlnand_release_device(aml_chip);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* driver device registration */
|
|
static struct platform_driver amlnf_driver = {
|
|
.probe = amlnf_driver_probe,
|
|
.remove = amlnf_driver_remove,
|
|
.shutdown = amlnf_driver_shutdown,
|
|
.driver = {
|
|
.name = DRV_AMLNFDEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = amlogic_nand_dt_match,
|
|
},
|
|
};
|
|
|
|
static int __init amlnf_module_init(void)
|
|
{
|
|
return platform_driver_register(&amlnf_driver);
|
|
}
|
|
|
|
static void __exit amlnf_module_exit(void)
|
|
{
|
|
platform_driver_unregister(&amlnf_driver);
|
|
}
|
|
|
|
module_init(amlnf_module_init);
|
|
module_exit(amlnf_module_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("AML NAND TEAM");
|
|
MODULE_DESCRIPTION("aml nand flash driver");
|
|
#endif /* AML_NAND_UBOOT */
|
|
|
|
#ifdef AML_NAND_UBOOT
|
|
struct amlnand_phydev *aml_phy_get_dev(char * name)
|
|
{
|
|
struct amlnand_phydev * phy_dev = NULL;
|
|
|
|
list_for_each_entry(phy_dev, &nphy_dev_list, list){
|
|
if (!strncmp((char*)phy_dev->name, name, MAX_DEVICE_NAME_LEN)) {
|
|
aml_nand_dbg("nand get phy dev %s ",name);
|
|
return phy_dev;
|
|
}
|
|
}
|
|
aml_nand_msg("nand get phy dev %s failed",name);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct amlnf_dev* aml_nftl_get_dev(char * name)
|
|
{
|
|
struct amlnf_dev * nf_dev = NULL;
|
|
|
|
list_for_each_entry(nf_dev, &nf_dev_list, list){
|
|
if (!strcmp((char*)nf_dev->name, name)) {
|
|
aml_nand_dbg("nand get nftl dev %s ",name);
|
|
return nf_dev;
|
|
}
|
|
}
|
|
aml_nand_msg("nand get nftl dev %s failed",name);
|
|
return NULL;
|
|
}
|
|
|
|
void show_ldev_list(void)
|
|
{
|
|
struct amlnf_dev * nf_dev = NULL;
|
|
int i = 0;
|
|
printf("logic partitions table, in sectors\n");
|
|
list_for_each_entry(nf_dev, &nf_dev_list, list) {
|
|
printf("%d: %s: 0x%llx 0x%llx\n", i++, (char*)nf_dev->name,
|
|
nf_dev->offset_sector, nf_dev->size_sector);
|
|
}
|
|
}
|
|
|
|
|
|
int amlnf_logicdev_mis_init(struct amlnf_logicdev_t *amlnf_logicdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|