You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
7.5 KiB
362 lines
7.5 KiB
/*
|
|
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
|
|
#include "rkflash_api.h"
|
|
#include "rkflash_blk.h"
|
|
#include "rkflash_debug.h"
|
|
|
|
#ifdef CONFIG_RKSFC_NOR
|
|
|
|
#define IDB_ALIGN_64 128 /* 64 KB */
|
|
#define IDB_ALIGN_32 64 /* 32 KB */
|
|
|
|
static void P_RC4(u8 *buf, u16 len)
|
|
{
|
|
u8 S[256], K[256], temp;
|
|
u16 i, j, t, x;
|
|
u8 key[16] = {124, 78, 3, 4, 85, 5, 9, 7,
|
|
45, 44, 123, 56, 23, 13, 23, 17};
|
|
|
|
j = 0;
|
|
for (i = 0; i < 256; i++) {
|
|
S[i] = (u8)i;
|
|
j &= 0x0f;
|
|
K[i] = key[j];
|
|
j++;
|
|
}
|
|
|
|
j = 0;
|
|
for (i = 0; i < 256; i++) {
|
|
j = (j + S[i] + K[i]) % 256;
|
|
temp = S[i];
|
|
S[i] = S[j];
|
|
S[j] = temp;
|
|
}
|
|
|
|
i = 0;
|
|
j = 0;
|
|
for (x = 0; x < len; x++) {
|
|
i = (i + 1) % 256;
|
|
j = (j + S[i]) % 256;
|
|
temp = S[i];
|
|
S[i] = S[j];
|
|
S[j] = temp;
|
|
t = (S[i] + (S[j] % 256)) % 256;
|
|
buf[x] = buf[x] ^ S[t];
|
|
}
|
|
}
|
|
|
|
int rksfc_nor_init(struct udevice *udev)
|
|
{
|
|
struct rkflash_info *priv = dev_get_priv(udev);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
struct snor_info_packet *packet;
|
|
struct id_block_tag *idb_tag;
|
|
int ret;
|
|
|
|
ret = snor_init(p_dev);
|
|
if (ret == SFC_OK && p_dev->read_lines == DATA_LINES_X1) {
|
|
idb_tag = kzalloc(NOR_SECS_PAGE * 512, GFP_KERNEL);
|
|
if (!idb_tag)
|
|
return SFC_OK;
|
|
|
|
if (sfc_get_version() >= SFC_VER_4)
|
|
snor_read(p_dev, IDB_ALIGN_32, NOR_SECS_PAGE,
|
|
idb_tag);
|
|
else
|
|
snor_read(p_dev, IDB_ALIGN_64, NOR_SECS_PAGE,
|
|
idb_tag);
|
|
packet = (struct snor_info_packet *)&idb_tag->dev_param[0];
|
|
if (idb_tag->id == IDB_BLOCK_TAG_ID) {
|
|
P_RC4((u8 *)idb_tag, sizeof(struct id_block_tag));
|
|
snor_reinit_from_table_packet(p_dev, packet);
|
|
rkflash_print_error("snor reinit, ret= %d\n", ret);
|
|
}
|
|
kfree(idb_tag);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
u32 rksfc_nor_get_capacity(struct udevice *udev)
|
|
{
|
|
struct rkflash_info *priv = dev_get_priv(udev);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
|
|
return snor_get_capacity(p_dev);
|
|
}
|
|
|
|
int rksfc_nor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data)
|
|
{
|
|
u32 ret;
|
|
u32 offset, count = 0;
|
|
char *buf = (char *)p_data;
|
|
struct rkflash_info *priv = dev_get_priv(udev);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
|
|
if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
|
|
sec > FLASH_VENDOR_PART_END) {
|
|
ret = snor_read(p_dev, sec, n_sec, p_data);
|
|
if (ret != n_sec)
|
|
return ret;
|
|
} else {
|
|
memset(p_data, 0, 512 * n_sec);
|
|
if (sec < FLASH_VENDOR_PART_START) {
|
|
count = FLASH_VENDOR_PART_START - sec;
|
|
buf = (char *)p_data;
|
|
ret = snor_read(p_dev, sec, count, buf);
|
|
if (ret != count)
|
|
return ret;
|
|
}
|
|
if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
|
|
count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
|
|
offset = FLASH_VENDOR_PART_END - sec + 1;
|
|
buf = (char *)p_data + offset * 512;
|
|
ret = snor_read(p_dev,
|
|
FLASH_VENDOR_PART_END + 1,
|
|
count, buf);
|
|
if (ret != count)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return n_sec;
|
|
}
|
|
|
|
/* Workaround for GPT not aligned program */
|
|
int rksfc_nor_simply_over_write(struct udevice *udev,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
const void *p_data)
|
|
{
|
|
struct rkflash_info *priv = dev_get_priv(udev);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
u8 *pbuf_temp;
|
|
u32 addr_aligned, offset, remain;
|
|
|
|
addr_aligned = sec / NOR_SECS_PAGE * NOR_SECS_PAGE;
|
|
offset = sec - addr_aligned;
|
|
remain = (offset + n_sec + NOR_SECS_PAGE - 1) / NOR_SECS_PAGE * NOR_SECS_PAGE;
|
|
|
|
pbuf_temp = malloc(remain * 512);
|
|
snor_read(p_dev, addr_aligned, remain, pbuf_temp);
|
|
memcpy(pbuf_temp + offset * 512, p_data, n_sec * 512);
|
|
snor_write(p_dev, addr_aligned, remain, pbuf_temp);
|
|
free(pbuf_temp);
|
|
|
|
return n_sec;
|
|
}
|
|
|
|
int rksfc_nor_write(struct udevice *udev,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
const void *p_data)
|
|
{
|
|
u32 ret;
|
|
u32 offset, count = 0;
|
|
char *buf = (char *)p_data;
|
|
struct rkflash_info *priv = dev_get_priv(udev);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
u32 sfc_nor_density = rksfc_nor_get_capacity(udev);
|
|
|
|
if (sec >= (sfc_nor_density - 33))
|
|
return rksfc_nor_simply_over_write(udev, sec, n_sec, p_data);
|
|
|
|
if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
|
|
sec > FLASH_VENDOR_PART_END) {
|
|
ret = snor_write(p_dev, sec, n_sec, (void *)p_data);
|
|
if (ret != n_sec)
|
|
return ret;
|
|
} else {
|
|
if (sec < FLASH_VENDOR_PART_START) {
|
|
count = FLASH_VENDOR_PART_START - sec;
|
|
buf = (char *)p_data;
|
|
ret = snor_write(p_dev, sec, count, buf);
|
|
if (ret != count)
|
|
return ret;
|
|
}
|
|
if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
|
|
count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
|
|
offset = FLASH_VENDOR_PART_END - sec + 1;
|
|
buf = (char *)p_data + offset * 512;
|
|
ret = snor_write(p_dev,
|
|
FLASH_VENDOR_PART_END + 1,
|
|
count, buf);
|
|
if (ret != count)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return n_sec;
|
|
}
|
|
|
|
int rksfc_nor_vendor_read(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
|
|
return snor_read(p_dev, sec, n_sec, p_data);
|
|
}
|
|
|
|
int rksfc_nor_vendor_write(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
|
|
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
|
|
|
return snor_write(p_dev, sec, n_sec, p_data);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_RKSFC_NAND
|
|
int rksfc_nand_init(struct udevice *udev)
|
|
{
|
|
int ret;
|
|
|
|
ret = sfc_nand_init();
|
|
if (ret) {
|
|
return ret;
|
|
} else {
|
|
sfc_nand_ftl_ops_init();
|
|
|
|
return sftl_init();
|
|
}
|
|
}
|
|
|
|
int rksfc_nand_read(struct udevice *udev, u32 index, u32 count, void *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_read(index, count, (u8 *)buf);
|
|
if (!ret)
|
|
return count;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
int rksfc_nand_write(struct udevice *udev,
|
|
u32 index,
|
|
u32 count,
|
|
const void *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_write(index, count, (u8 *)buf);
|
|
if (!ret)
|
|
return count;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
u32 rksfc_nand_get_density(struct udevice *udev)
|
|
{
|
|
return sftl_get_density();
|
|
}
|
|
|
|
int rksfc_nand_vendor_read(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_vendor_read(sec, n_sec, (u8 *)p_data);
|
|
if (!ret)
|
|
return n_sec;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
int rksfc_nand_vendor_write(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_vendor_write(sec, n_sec, (u8 *)p_data);
|
|
if (!ret)
|
|
return n_sec;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_RKNANDC_NAND
|
|
int rknand_flash_init(struct udevice *udev)
|
|
{
|
|
return sftl_init();
|
|
}
|
|
|
|
int rknand_flash_read(struct udevice *udev, u32 index, u32 count, void *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_read(index, count, (u8 *)buf);
|
|
if (!ret)
|
|
return count;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
int rknand_flash_write(struct udevice *udev,
|
|
u32 index,
|
|
u32 count,
|
|
const void *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_write(index, count, (u8 *)buf);
|
|
if (!ret)
|
|
return count;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
u32 rknand_flash_get_density(struct udevice *udev)
|
|
{
|
|
return sftl_get_density();
|
|
}
|
|
|
|
int rknand_flash_vendor_read(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_vendor_read(sec, n_sec, (u8 *)p_data);
|
|
if (!ret)
|
|
return n_sec;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
int rknand_flash_vendor_write(struct blk_desc *dev_desc,
|
|
u32 sec,
|
|
u32 n_sec,
|
|
void *p_data)
|
|
{
|
|
int ret;
|
|
|
|
ret = sftl_vendor_write(sec, n_sec, (u8 *)p_data);
|
|
if (!ret)
|
|
return n_sec;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
#endif
|
|
|