rockchip: rkflash: add rk small capacity flash storage support with block interface
Currently only support spi nor flash, but slc nand flash and spi nand flash will be supported in next step; The code includes block layer, ftl layer(nand flash), controller layer and flash layer. Change-Id: Iaa56294ee2a5a6dfec2d0172efc35de30c88365a Signed-off-by: Dingqiang Lin <jon.lin@rock-chips.com>
This commit is contained in:
parent
31c3ca3223
commit
ad309a883b
|
|
@ -76,6 +76,8 @@ source "drivers/reset/Kconfig"
|
|||
|
||||
source "drivers/rknand/Kconfig"
|
||||
|
||||
source "drivers/rkflash/Kconfig"
|
||||
|
||||
source "drivers/rtc/Kconfig"
|
||||
|
||||
source "drivers/scsi/Kconfig"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ obj-y += misc/
|
|||
obj-$(CONFIG_MMC) += mmc/
|
||||
obj-$(CONFIG_NVME) += nvme/
|
||||
obj-$(CONFIG_RKNAND) += rknand/
|
||||
obj-$(CONFIG_RKFLASH) += rkflash/
|
||||
obj-y += pcmcia/
|
||||
obj-y += dfu/
|
||||
obj-$(CONFIG_X86) += pch/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
#
|
||||
if ARCH_ROCKCHIP
|
||||
|
||||
menuconfig RKFLASH
|
||||
tristate "Rockchip Flash Devices Support"
|
||||
default n
|
||||
help
|
||||
Enable rockchip flash devices support.
|
||||
rkflash driver support 3-type flash devices: NANDC NAND, SFC_NOR
|
||||
and SFC_NAND.
|
||||
Say Y when you have a board with one of them.
|
||||
|
||||
if RKFLASH
|
||||
|
||||
comment "Rockchip Flash Devices"
|
||||
|
||||
config RKSFC_NOR
|
||||
bool "Rockchip SFC NOR flash support"
|
||||
depends on BLK
|
||||
help
|
||||
This option enables support for Rockchip SFC nor devices.
|
||||
It's block interface.
|
||||
512Kb/sector.
|
||||
Say y here to enable rockchip spi nor flash driver.
|
||||
|
||||
endif # RKFLASH
|
||||
|
||||
endif # ARCH_ROCKCHIP
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
#
|
||||
|
||||
obj-y += rkflash_debug.o
|
||||
obj-$(CONFIG_RKSFC_NOR) += sfc.o sfc_nor.o rkflash_api.o rksfc_base.o rkflash_blk.o
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
|
||||
#include "rkflash_api.h"
|
||||
#include "rkflash_blk.h"
|
||||
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
int rk_snor_init(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_init(p_dev);
|
||||
}
|
||||
|
||||
u32 rk_snor_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 rk_snor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
|
||||
|
||||
return snor_read(p_dev, sec, n_sec, p_data);
|
||||
}
|
||||
|
||||
int rk_snor_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;
|
||||
|
||||
return snor_write(p_dev, sec, n_sec, p_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#ifndef __RKFLASH_API_H
|
||||
#define __RKFLASH_API_H
|
||||
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
#include "sfc_nor.h"
|
||||
#include "sfc.h"
|
||||
|
||||
int rk_snor_init(struct udevice *udev);
|
||||
u32 rk_snor_get_capacity(struct udevice *udev);
|
||||
int rk_snor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data);
|
||||
int rk_snor_write(struct udevice *udev, u32 sec, u32 n_sec, const void *p_data);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
#include "rkflash_blk.h"
|
||||
#include "rkflash_debug.h"
|
||||
|
||||
ulong rkflash_bread(struct udevice *udev, lbaint_t start,
|
||||
lbaint_t blkcnt, void *dst)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
|
||||
if (!priv->read)
|
||||
return 0;
|
||||
|
||||
return (ulong)priv->read(udev->parent, (u32)start, (u32)blkcnt, dst);
|
||||
}
|
||||
|
||||
ulong rkflash_bwrite(struct udevice *udev, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *src)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
|
||||
if (!priv->write)
|
||||
return 0;
|
||||
|
||||
return (ulong)priv->write(udev->parent, (u32)start, (u32)blkcnt, src);
|
||||
}
|
||||
|
||||
ulong rkflash_berase(struct udevice *udev, lbaint_t start,
|
||||
lbaint_t blkcnt)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
|
||||
if (blkcnt == 0)
|
||||
return 0;
|
||||
|
||||
if ((start + blkcnt) > block_dev->lba)
|
||||
return 0;
|
||||
|
||||
if (!priv->erase)
|
||||
return 0;
|
||||
|
||||
return (ulong)priv->erase(udev->parent, (u32)start, (u32)blkcnt);
|
||||
}
|
||||
|
||||
static int rkflash_blk_probe(struct udevice *udev)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(udev);
|
||||
|
||||
debug("%s %d %p ndev = %p %p\n", __func__, __LINE__,
|
||||
udev, priv, udev->parent);
|
||||
priv->child_dev = udev;
|
||||
if (priv->flash_con_type == FLASH_CON_TYPE_SFC)
|
||||
desc->if_type = IF_TYPE_RKSFC;
|
||||
desc->lba = priv->density;
|
||||
desc->log2blksz = 9;
|
||||
desc->blksz = 512;
|
||||
desc->bdev = udev;
|
||||
desc->devnum = 0;
|
||||
sprintf(desc->vendor, "0x%.4x", 0x0308);
|
||||
memcpy(desc->product, "rkflash", sizeof("rkflash"));
|
||||
memcpy(desc->revision, "V1.00", sizeof("V1.00"));
|
||||
part_init(desc);
|
||||
rkflash_test(udev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct blk_ops rkflash_blk_ops = {
|
||||
.read = rkflash_bread,
|
||||
.write = rkflash_bwrite,
|
||||
.erase = rkflash_berase,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rkflash_blk) = {
|
||||
.name = "rkflash_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &rkflash_blk_ops,
|
||||
.probe = rkflash_blk_probe,
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#ifndef __RKFLASH_BLK_H__
|
||||
#define __RKFLASH_BLK_H__
|
||||
|
||||
enum flash_con_type {
|
||||
FLASH_CON_TYPE_NANDC = 0,
|
||||
FLASH_CON_TYPE_SFC,
|
||||
FLASH_CON_TYPE_MAX,
|
||||
};
|
||||
|
||||
enum flash_type {
|
||||
FLASH_TYPE_NANDC_NAND = 0,
|
||||
FLASH_TYPE_SFC_NOR,
|
||||
FLASH_TYPE_SFC_NAND,
|
||||
FLASH_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct flash_operation {
|
||||
int id;
|
||||
int (*flash_init)(struct udevice *udev);
|
||||
u32 (*flash_get_capacity)(struct udevice *udev);
|
||||
int (*flash_read)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
void *buffer);
|
||||
int (*flash_write)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
const void *buffer);
|
||||
};
|
||||
|
||||
struct rkflash_dev {
|
||||
u8 reserved[128];
|
||||
};
|
||||
|
||||
struct rkflash_info {
|
||||
void *ioaddr;
|
||||
u32 flash_con_type;
|
||||
u32 freq;
|
||||
u32 density;
|
||||
struct udevice *child_dev;
|
||||
struct rkflash_dev flash_dev_info;
|
||||
/*
|
||||
* read() - read from a block device
|
||||
*
|
||||
* @start: Start block number to read (0=first)
|
||||
* @blkcnt: Number of blocks to read
|
||||
* @buffer: Destination buffer for data read
|
||||
* @return 0 is OK, -1 is error.
|
||||
*/
|
||||
int (*read)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
void *buffer);
|
||||
/*
|
||||
* write() - write to a block device
|
||||
*
|
||||
* @dev: Device to write to
|
||||
* @start: Start block number to write (0=first)
|
||||
* @blkcnt: Number of blocks to write
|
||||
* @buffer: Source buffer for data to write
|
||||
* @return 0 is OK, -1 is error.
|
||||
*/
|
||||
int (*write)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt,
|
||||
const void *buffer);
|
||||
/*
|
||||
* erase() - erase a section of a block device
|
||||
*
|
||||
* @dev: Device to (partially) erase
|
||||
* @start: Start block number to erase (0=first)
|
||||
* @blkcnt: Number of blocks to erase
|
||||
* @return 0 is OK, -1 is error.
|
||||
*/
|
||||
int (*erase)(struct udevice *udev,
|
||||
u32 start,
|
||||
u32 blkcnt);
|
||||
};
|
||||
|
||||
struct rkflash_uclass_priv {
|
||||
struct rkflash_info *ndev;
|
||||
};
|
||||
|
||||
#endif /* __RKSFC_BLK_H__ */
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#include <blk.h>
|
||||
|
||||
#include "rkflash_debug.h"
|
||||
#include "rkflash_blk.h"
|
||||
|
||||
void rkflash_print_hex(char *s, void *buf, u32 width, u32 len)
|
||||
{
|
||||
u32 i, j;
|
||||
char *p8 = (char *)buf;
|
||||
short *p16 = (short *)buf;
|
||||
u32 *p32 = (u32 *)buf;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (j == 0)
|
||||
printf("%s 0x%x:", s, i * width);
|
||||
|
||||
if (width == 4)
|
||||
printf("%x ", p32[i]);
|
||||
else if (width == 2)
|
||||
printf("%x ", p16[i]);
|
||||
else
|
||||
printf("%02x ", p8[i]);
|
||||
if (++j >= 16) {
|
||||
j = 0;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#if (BLK_STRESS_TEST_EN)
|
||||
#define max_test_sector 64
|
||||
u8 pwrite[max_test_sector * 512];
|
||||
u8 pread[max_test_sector * 512];
|
||||
u32 *pwrite32;
|
||||
void blk_stress_test(struct udevice *udev)
|
||||
{
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(udev);
|
||||
struct rkflash_info *priv = dev_get_priv(udev->parent);
|
||||
u16 i, j, loop = 0;
|
||||
u32 test_end_lba;
|
||||
u32 test_lba = 0;
|
||||
u16 test_sec_count = 1;
|
||||
u16 print_flag;
|
||||
|
||||
if (!priv || !block_dev) {
|
||||
printf("device unknown\n");
|
||||
return;
|
||||
}
|
||||
|
||||
test_end_lba = priv->density;
|
||||
pwrite32 = (u32 *)pwrite;
|
||||
for (i = 0; i < (max_test_sector * 512); i++)
|
||||
pwrite[i] = i;
|
||||
for (loop = 0; loop < 10; loop++) {
|
||||
printf("---------Test loop = %d---------\n", loop);
|
||||
printf("---------Test ftl write---------\n");
|
||||
test_sec_count = 1;
|
||||
printf("test_end_lba = %x\n", test_end_lba);
|
||||
printf("test_lba = %x\n", test_lba);
|
||||
for (test_lba = 0;
|
||||
(test_lba + test_sec_count) < test_end_lba;) {
|
||||
pwrite32[0] = test_lba;
|
||||
blk_dwrite(block_dev, test_lba, test_sec_count, pwrite);
|
||||
blk_dread(block_dev, test_lba, test_sec_count, pread);
|
||||
for (j = 0; j < test_sec_count * 512; j++) {
|
||||
if (pwrite[j] != pread[j]) {
|
||||
rkflash_print_hex("w:",
|
||||
pwrite,
|
||||
4,
|
||||
test_sec_count * 128);
|
||||
rkflash_print_hex("r:",
|
||||
pread,
|
||||
4,
|
||||
test_sec_count * 128);
|
||||
printf("r=%x, n=%x, w=%x, r=%x\n",
|
||||
test_lba,
|
||||
j,
|
||||
pwrite[j],
|
||||
pread[j]);
|
||||
while (1)
|
||||
;
|
||||
break;
|
||||
}
|
||||
}
|
||||
print_flag = test_lba & 0x1FF;
|
||||
if (print_flag < test_sec_count)
|
||||
printf("test_lba = %x\n", test_lba);
|
||||
test_lba += test_sec_count;
|
||||
test_sec_count++;
|
||||
if (test_sec_count > max_test_sector)
|
||||
test_sec_count = 1;
|
||||
}
|
||||
printf("---------Test ftl check---------\n");
|
||||
|
||||
test_sec_count = 1;
|
||||
for (test_lba = 0;
|
||||
(test_lba + test_sec_count) < test_end_lba;) {
|
||||
pwrite32[0] = test_lba;
|
||||
blk_dread(block_dev, test_lba, test_sec_count, pread);
|
||||
print_flag = test_lba & 0x7FF;
|
||||
if (print_flag < test_sec_count)
|
||||
printf("test_lba = %x\n", test_lba);
|
||||
|
||||
for (j = 0; j < test_sec_count * 512; j++) {
|
||||
if (pwrite[j] != pread[j]) {
|
||||
printf("r=%x, n=%x, w=%x, r=%x\n",
|
||||
test_lba,
|
||||
j,
|
||||
pwrite[j],
|
||||
pread[j]);
|
||||
/* while(1); */
|
||||
break;
|
||||
}
|
||||
}
|
||||
test_lba += test_sec_count;
|
||||
test_sec_count++;
|
||||
if (test_sec_count > max_test_sector)
|
||||
test_sec_count = 1;
|
||||
}
|
||||
}
|
||||
printf("---------Test end---------\n");
|
||||
/* while(1); */
|
||||
}
|
||||
#endif
|
||||
|
||||
void rkflash_test(struct udevice *udev)
|
||||
{
|
||||
#if (BLK_STRESS_TEST_EN)
|
||||
blk_stress_test(udev);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#ifndef _RKFLASH_DEBUG_H
|
||||
#define _RKFLASH_DEBUG_H
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
|
||||
/*
|
||||
* Print switch, set to 1 if needed
|
||||
* I - info
|
||||
* E - error
|
||||
* HEX - multiline print
|
||||
*/
|
||||
|
||||
#define PRINT_SWI_SFC_I 0
|
||||
#define PRINT_SWI_SFC_E 1
|
||||
#define PRINT_SWI_SFC_HEX 1
|
||||
|
||||
/*
|
||||
* Test switch
|
||||
*/
|
||||
#define BLK_STRESS_TEST_EN 0
|
||||
|
||||
#if (RINT_SWI_SFC_I)
|
||||
#define PRINT_SFC_I(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_SFC_I(...)
|
||||
#endif
|
||||
|
||||
#if (PRINT_SWI_SFC_E)
|
||||
#define PRINT_SFC_E(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINT_SFC_E(...)
|
||||
#endif
|
||||
|
||||
#if (PRINT_SWI_SFC_HEX)
|
||||
#define PRINT_SFC_HEX(s, buf, width, len)\
|
||||
rkflash_print_hex(s, buf, width, len)
|
||||
#else
|
||||
#define PRINT_SFC_HEX(s, buf, width, len)
|
||||
#endif
|
||||
|
||||
void rkflash_print_hex(char *s, void *buf, u32 width, u32 len);
|
||||
void rkflash_test(struct udevice *p_dev);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
#include "rkflash_blk.h"
|
||||
#include "rkflash_api.h"
|
||||
|
||||
static struct flash_operation spi_flash_op = {
|
||||
#ifdef CONFIG_RKSFC_NOR
|
||||
FLASH_TYPE_SFC_NOR,
|
||||
rk_snor_init,
|
||||
rk_snor_get_capacity,
|
||||
rk_snor_read,
|
||||
rk_snor_write,
|
||||
#else
|
||||
-1, NULL, NULL, NULL, NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
int rksfc_scan_namespace(void)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_SPI_FLASH, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
debug("%s %d %p\n", __func__, __LINE__, dev);
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rksfc_blk_bind(struct udevice *udev)
|
||||
{
|
||||
struct udevice *bdev;
|
||||
int ret;
|
||||
|
||||
ret = blk_create_devicef(udev, "rkflash_blk", "blk",
|
||||
IF_TYPE_RKSFC,
|
||||
0, 512, 0, &bdev);
|
||||
if (ret) {
|
||||
debug("Cannot create block device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_rksfc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rkflash_info *priv = dev_get_priv(dev);
|
||||
|
||||
priv->ioaddr = dev_read_addr_ptr(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_rksfc_probe(struct udevice *udev)
|
||||
{
|
||||
int ret;
|
||||
struct rkflash_info *priv = dev_get_priv(udev);
|
||||
|
||||
debug("%s %d %p ndev = %p\n", __func__, __LINE__, udev, priv);
|
||||
|
||||
sfc_init(priv->ioaddr);
|
||||
if (spi_flash_op.id == -1) {
|
||||
debug("%s no optional spi flash\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
ret = spi_flash_op.flash_init(udev);
|
||||
if (!ret) {
|
||||
priv->flash_con_type = FLASH_CON_TYPE_SFC;
|
||||
priv->density = spi_flash_op.flash_get_capacity(udev);
|
||||
priv->read = spi_flash_op.flash_read;
|
||||
priv->write = spi_flash_op.flash_write;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(rksfc) = {
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.name = "rksfc",
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_sfc_ids[] = {
|
||||
{ .compatible = "rockchip,rksfc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rksfc) = {
|
||||
.name = "rksfc",
|
||||
.id = UCLASS_SPI_FLASH,
|
||||
.of_match = rockchip_sfc_ids,
|
||||
.bind = rksfc_blk_bind,
|
||||
.probe = rockchip_rksfc_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct rkflash_info),
|
||||
.ofdata_to_platdata = rockchip_rksfc_ofdata_to_platdata,
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/delay.h>
|
||||
#include <bouncebuf.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "sfc.h"
|
||||
|
||||
static void __iomem *g_sfc_reg;
|
||||
|
||||
static void sfc_reset(void)
|
||||
{
|
||||
int timeout = 10000;
|
||||
|
||||
writel(SFC_RESET, g_sfc_reg + SFC_RCVR);
|
||||
while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) {
|
||||
sfc_delay(1);
|
||||
timeout--;
|
||||
}
|
||||
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
|
||||
}
|
||||
|
||||
u16 sfc_get_version(void)
|
||||
{
|
||||
return (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff);
|
||||
}
|
||||
|
||||
int sfc_init(void __iomem *reg_addr)
|
||||
{
|
||||
g_sfc_reg = reg_addr;
|
||||
sfc_reset();
|
||||
writel(0, g_sfc_reg + SFC_CTRL);
|
||||
|
||||
return SFC_OK;
|
||||
}
|
||||
|
||||
void sfc_clean_irq(void)
|
||||
{
|
||||
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
|
||||
writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR);
|
||||
}
|
||||
|
||||
int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
union SFCCMD_DATA cmd;
|
||||
int reg;
|
||||
int timeout = 0;
|
||||
|
||||
reg = readl(g_sfc_reg + SFC_FSR);
|
||||
if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) ||
|
||||
(readl(g_sfc_reg + SFC_SR) & SFC_BUSY))
|
||||
sfc_reset();
|
||||
|
||||
cmd.d32 = sfcmd;
|
||||
if (cmd.b.addrbits == SFC_ADDR_XBITS) {
|
||||
union SFCCTRL_DATA ctrl;
|
||||
|
||||
ctrl.d32 = sfctrl;
|
||||
if (!ctrl.b.addrbits)
|
||||
return SFC_PARAM_ERR;
|
||||
/* Controller plus 1 automatically */
|
||||
writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT);
|
||||
}
|
||||
/* shift in the data at negedge sclk_out */
|
||||
sfctrl |= 0x2;
|
||||
|
||||
writel(sfctrl, g_sfc_reg + SFC_CTRL);
|
||||
writel(sfcmd, g_sfc_reg + SFC_CMD);
|
||||
if (cmd.b.addrbits)
|
||||
writel(addr, g_sfc_reg + SFC_ADDR);
|
||||
if (!cmd.b.datasize)
|
||||
goto exit_wait;
|
||||
if (SFC_ENABLE_DMA & sfctrl) {
|
||||
struct bounce_buffer bb;
|
||||
unsigned int bb_flags;
|
||||
|
||||
if (cmd.b.rw == SFC_WRITE)
|
||||
bb_flags = GEN_BB_READ;
|
||||
else
|
||||
bb_flags = GEN_BB_WRITE;
|
||||
|
||||
ret = bounce_buffer_start(&bb, data, cmd.b.datasize, bb_flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
|
||||
writel(~((u32)FINISH_INT), g_sfc_reg + SFC_IMR);
|
||||
writel((u64)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR);
|
||||
writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER);
|
||||
|
||||
timeout = cmd.b.datasize * 10;
|
||||
while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) &&
|
||||
(timeout-- > 0))
|
||||
sfc_delay(1);
|
||||
writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR);
|
||||
if (timeout <= 0)
|
||||
ret = SFC_WAIT_TIMEOUT;
|
||||
bounce_buffer_stop(&bb);
|
||||
} else {
|
||||
u32 i, words, count, bytes;
|
||||
union SFCFSR_DATA fifostat;
|
||||
u32 *p_data = (u32 *)data;
|
||||
|
||||
if (cmd.b.rw == SFC_WRITE) {
|
||||
words = (cmd.b.datasize + 3) >> 2;
|
||||
while (words) {
|
||||
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
|
||||
if (fifostat.b.txlevel > 0) {
|
||||
count = words < fifostat.b.txlevel ?
|
||||
words : fifostat.b.txlevel;
|
||||
for (i = 0; i < count; i++) {
|
||||
writel(*p_data++,
|
||||
g_sfc_reg + SFC_DATA);
|
||||
words--;
|
||||
}
|
||||
if (words == 0)
|
||||
break;
|
||||
timeout = 0;
|
||||
} else {
|
||||
sfc_delay(1);
|
||||
if (timeout++ > 10000) {
|
||||
ret = SFC_TX_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* SFC_READ == cmd.b.rw */
|
||||
bytes = cmd.b.datasize & 0x3;
|
||||
words = cmd.b.datasize >> 2;
|
||||
while (words) {
|
||||
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
|
||||
if (fifostat.b.rxlevel > 0) {
|
||||
u32 count;
|
||||
|
||||
count = words < fifostat.b.rxlevel ?
|
||||
words : fifostat.b.rxlevel;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
*p_data++ = readl(g_sfc_reg +
|
||||
SFC_DATA);
|
||||
words--;
|
||||
}
|
||||
if (words == 0)
|
||||
break;
|
||||
timeout = 0;
|
||||
} else {
|
||||
sfc_delay(1);
|
||||
if (timeout++ > 10000) {
|
||||
ret = SFC_RX_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
while (bytes) {
|
||||
fifostat.d32 = readl(g_sfc_reg + SFC_FSR);
|
||||
if (fifostat.b.rxlevel > 0) {
|
||||
u8 *p_data1 = (u8 *)p_data;
|
||||
|
||||
words = readl(g_sfc_reg + SFC_DATA);
|
||||
for (i = 0; i < bytes; i++)
|
||||
p_data1[i] =
|
||||
(u8)((words >> (i * 8)) & 0xFF);
|
||||
break;
|
||||
}
|
||||
|
||||
sfc_delay(1);
|
||||
if (timeout++ > 10000) {
|
||||
ret = SFC_RX_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit_wait:
|
||||
timeout = 0; /* wait cmd or data send complete */
|
||||
while (!(readl(g_sfc_reg + SFC_FSR) & SFC_TXEMPTY)) {
|
||||
sfc_delay(1);
|
||||
if (timeout++ > 100000) { /* wait 100ms */
|
||||
ret = SFC_TX_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sfc_delay(1); /* CS# High Time (read/write) >100ns */
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#ifndef _SFC_H
|
||||
#define _SFC_H
|
||||
|
||||
#define SFC_VER_3 0x3 /* ver 3, else ver 1 */
|
||||
|
||||
#define SFC_MAX_IOSIZE (1024 * 8) /* 8K byte */
|
||||
#define SFC_EN_INT (0) /* enable interrupt */
|
||||
#define SFC_EN_DMA (1) /* enable dma */
|
||||
#define SFC_FIFO_DEPTH (0x10) /* 16 words */
|
||||
|
||||
/* FIFO watermark */
|
||||
#define SFC_RX_WMARK (SFC_FIFO_DEPTH) /* RX watermark level */
|
||||
#define SFC_TX_WMARK (SFC_FIFO_DEPTH) /* TX watermark level */
|
||||
#define SFC_RX_WMARK_SHIFT (8)
|
||||
#define SFC_TX_WMARK_SHIFT (0)
|
||||
|
||||
/*return value*/
|
||||
#define SFC_OK (0)
|
||||
#define SFC_ERROR (-1)
|
||||
#define SFC_PARAM_ERR (-2)
|
||||
#define SFC_TX_TIMEOUT (-3)
|
||||
#define SFC_RX_TIMEOUT (-4)
|
||||
#define SFC_WAIT_TIMEOUT (-5)
|
||||
#define SFC_BUSY_TIMEOUT (-6)
|
||||
#define SFC_ECC_FAIL (-7)
|
||||
#define SFC_PROG_FAIL (-8)
|
||||
#define SFC_ERASE_FAIL (-9)
|
||||
|
||||
/* SFC_CMD Register */
|
||||
#define SFC_ADDR_0BITS (0)
|
||||
#define SFC_ADDR_24BITS (1)
|
||||
#define SFC_ADDR_32BITS (2)
|
||||
#define SFC_ADDR_XBITS (3)
|
||||
|
||||
#define SFC_WRITE (1)
|
||||
#define SFC_READ (0)
|
||||
|
||||
/* SFC_CTRL Register */
|
||||
#define SFC_1BITS_LINE (0)
|
||||
#define SFC_2BITS_LINE (1)
|
||||
#define SFC_4BITS_LINE (2)
|
||||
|
||||
#define SFC_ENABLE_DMA BIT(14)
|
||||
#define sfc_delay(us) udelay(us)
|
||||
|
||||
#define DMA_INT BIT(7) /* dma interrupt */
|
||||
#define NSPIERR_INT BIT(6) /* Nspi error interrupt */
|
||||
#define AHBERR_INT BIT(5) /* Ahb bus error interrupt */
|
||||
#define FINISH_INT BIT(4) /* Transfer finish interrupt */
|
||||
#define TXEMPTY_INT BIT(3) /* Tx fifo empty interrupt */
|
||||
#define TXOF_INT BIT(2) /* Tx fifo overflow interrupt */
|
||||
#define RXUF_INT BIT(1) /* Rx fifo underflow interrupt */
|
||||
#define RXFULL_INT BIT(0) /* Rx fifo full interrupt */
|
||||
|
||||
/* SFC_FSR Register*/
|
||||
#define SFC_RXFULL BIT(3) /* rx fifo full */
|
||||
#define SFC_RXEMPTY BIT(2) /* rx fifo empty */
|
||||
#define SFC_TXEMPTY BIT(1) /* tx fifo empty */
|
||||
#define SFC_TXFULL BIT(0) /* tx fifo full */
|
||||
|
||||
/* SFC_RCVR Register */
|
||||
#define SFC_RESET BIT(0) /* controller reset */
|
||||
|
||||
/* SFC_SR Register */
|
||||
/* sfc busy flag. When busy, don't try to set the control register */
|
||||
#define SFC_BUSY BIT(0)
|
||||
|
||||
/* SFC_DMA_TRIGGER Register */
|
||||
/* Dma start trigger signal. Auto cleared after write */
|
||||
#define SFC_DMA_START BIT(0)
|
||||
|
||||
#define SFC_CTRL 0x00
|
||||
#define SFC_IMR 0x04
|
||||
#define SFC_ICLR 0x08
|
||||
#define SFC_FTLR 0x0C
|
||||
#define SFC_RCVR 0x10
|
||||
#define SFC_AX 0x14
|
||||
#define SFC_ABIT 0x18
|
||||
#define SFC_MASKISR 0x1C
|
||||
#define SFC_FSR 0x20
|
||||
#define SFC_SR 0x24
|
||||
#define SFC_RAWISR 0x28
|
||||
#define SFC_VER 0x2C
|
||||
#define SFC_QOP 0x30
|
||||
#define SFC_DMA_TRIGGER 0x80
|
||||
#define SFC_DMA_ADDR 0x84
|
||||
#define SFC_CMD 0x100
|
||||
#define SFC_ADDR 0x104
|
||||
#define SFC_DATA 0x108
|
||||
|
||||
union SFCFSR_DATA {
|
||||
u32 d32;
|
||||
struct {
|
||||
unsigned txempty : 1;
|
||||
unsigned txfull : 1;
|
||||
unsigned rxempty : 1;
|
||||
unsigned rxfull : 1;
|
||||
unsigned reserved7_4 : 4;
|
||||
unsigned txlevel : 5;
|
||||
unsigned reserved15_13 : 3;
|
||||
unsigned rxlevel : 5;
|
||||
unsigned reserved31_21 : 11;
|
||||
} b;
|
||||
};
|
||||
|
||||
/*------------------------------ Global Typedefs -----------------------------*/
|
||||
enum SFC_DATA_LINES {
|
||||
DATA_LINES_X1 = 0,
|
||||
DATA_LINES_X2,
|
||||
DATA_LINES_X4
|
||||
};
|
||||
|
||||
union SFCCTRL_DATA {
|
||||
/* raw register data */
|
||||
u32 d32;
|
||||
/* register bits */
|
||||
struct {
|
||||
/* spi mode select */
|
||||
unsigned mode : 1;
|
||||
/*
|
||||
* Shift in phase selection
|
||||
* 0: shift in the flash data at posedge sclk_out
|
||||
* 1: shift in the flash data at negedge sclk_out
|
||||
*/
|
||||
unsigned sps : 1;
|
||||
unsigned reserved3_2 : 2;
|
||||
/* sclk_idle_level_cycles */
|
||||
unsigned scic : 4;
|
||||
/* Cmd bits number */
|
||||
unsigned cmdlines : 2;
|
||||
/* Address bits number */
|
||||
unsigned addrlines : 2;
|
||||
/* Data bits number */
|
||||
unsigned datalines : 2;
|
||||
/* this bit is not exit in regiseter, just use for code param */
|
||||
unsigned enbledma : 1;
|
||||
unsigned reserved15 : 1;
|
||||
unsigned addrbits : 5;
|
||||
unsigned reserved31_21 : 11;
|
||||
} b;
|
||||
};
|
||||
|
||||
union SFCCMD_DATA {
|
||||
/* raw register data */
|
||||
u32 d32;
|
||||
/* register bits */
|
||||
struct {
|
||||
/* Command that will send to Serial Flash */
|
||||
unsigned cmd : 8;
|
||||
/* Dummy bits number */
|
||||
unsigned dummybits : 4;
|
||||
/* 0: read, 1: write */
|
||||
unsigned rw : 1;
|
||||
/* Continuous read mode */
|
||||
unsigned readmode : 1;
|
||||
/* Address bits number */
|
||||
unsigned addrbits : 2;
|
||||
/* Transferred bytes number */
|
||||
unsigned datasize : 14;
|
||||
/* Chip select */
|
||||
unsigned cs : 2;
|
||||
} b;
|
||||
};
|
||||
|
||||
int sfc_init(void __iomem *reg_addr);
|
||||
int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data);
|
||||
u16 sfc_get_version(void);
|
||||
void sfc_clean_irq(void);
|
||||
int rksfc_get_reg_addr(unsigned long *p_sfc_addr);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,612 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "sfc_nor.h"
|
||||
#include "rkflash_debug.h"
|
||||
#include "rkflash_blk.h"
|
||||
|
||||
static struct flash_info spi_flash_tbl[] = {
|
||||
/* GD25Q32B */
|
||||
{0xc84016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 13, 9, 0},
|
||||
/* GD25Q64B */
|
||||
{0xc84017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0},
|
||||
/* GD25Q127C and GD25Q128C*/
|
||||
{0xc84018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0},
|
||||
/* GD25Q256B */
|
||||
{0xc84019, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1C, 16, 6, 0},
|
||||
/* GD25Q512MC */
|
||||
{0xc84020, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1C, 17, 6, 0},
|
||||
/* 25Q128FV */
|
||||
{0xef4018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0},
|
||||
/* 25Q256FV */
|
||||
{0xef4019, 128, 8, 0x13, 0x02, 0x6C, 0x32, 0x20, 0xD8, 0x3C, 16, 9, 0},
|
||||
/* XT25F128A */
|
||||
{0x207018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x00, 15, 0, 0},
|
||||
/* MX25L25635E/F */
|
||||
{0xc22019, 128, 8, 0x03, 0x02, 0x6B, 0x38, 0x20, 0xD8, 0x30, 16, 6, 0},
|
||||
};
|
||||
|
||||
static const u8 sfnor_dev_code[] = {
|
||||
0x11,
|
||||
0x12,
|
||||
0x13,
|
||||
0x14,
|
||||
0x15,
|
||||
0x16,
|
||||
0x17,
|
||||
0x18,
|
||||
0x19
|
||||
};
|
||||
|
||||
static const u32 sfnor_capacity[] = {
|
||||
0x20000, /* 128k-byte */
|
||||
0x40000, /* 256k-byte */
|
||||
0x80000, /* 512k-byte */
|
||||
0x100000, /* 1M-byte */
|
||||
0x200000, /* 2M-byte */
|
||||
0x400000, /* 4M-byte */
|
||||
0x800000, /* 8M-byte */
|
||||
0x1000000, /* 16M-byte */
|
||||
0x2000000 /* 32M-byte */
|
||||
};
|
||||
|
||||
static struct flash_info *g_spi_flash_info;
|
||||
|
||||
static int snor_write_en(void)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_WRITE_EN;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_enter_4byte_mode(void)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_ENTER_4BYTE_MODE;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_read_status(u32 reg_index, u8 *status)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u8 read_stat_cmd[] = {CMD_READ_STATUS,
|
||||
CMD_READ_STATUS2, CMD_READ_STATUS3};
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = read_stat_cmd[reg_index];
|
||||
sfcmd.b.datasize = 1;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, status);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_wait_busy(int timeout)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u32 i, status;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_READ_STATUS;
|
||||
sfcmd.b.datasize = 1;
|
||||
|
||||
for (i = 0; i < timeout; i++) {
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
if ((status & 0x01) == 0)
|
||||
return SFC_OK;
|
||||
|
||||
sfc_delay(1);
|
||||
}
|
||||
PRINT_SFC_E("%s error %x\n", __func__, timeout);
|
||||
|
||||
return SFC_BUSY_TIMEOUT;
|
||||
}
|
||||
|
||||
static int snor_write_status2(u32 reg_index, u8 status)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u8 status2[2];
|
||||
u8 read_index;
|
||||
|
||||
status2[reg_index] = status;
|
||||
read_index = (reg_index == 0) ? 1 : 0;
|
||||
ret = snor_read_status(read_index, &status2[read_index]);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
snor_write_en();
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_WRITE_STATUS;
|
||||
sfcmd.b.datasize = 2;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, &status2[0]);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
ret = snor_wait_busy(10000); /* 10ms */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_write_status(u32 reg_index, u8 status)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
u8 write_stat_cmd[] = {CMD_WRITE_STATUS,
|
||||
CMD_WRITE_STATUS2, CMD_WRITE_STATUS3};
|
||||
snor_write_en();
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = write_stat_cmd[reg_index];
|
||||
sfcmd.b.datasize = 1;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
ret = snor_wait_busy(10000); /* 10ms */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_erase(struct SFNOR_DEV *p_dev,
|
||||
u32 addr,
|
||||
enum NOR_ERASE_TYPE erase_type)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
int timeout[] = {400, 2000, 40000}; /* ms */
|
||||
|
||||
if (erase_type > ERASE_CHIP)
|
||||
return SFC_PARAM_ERR;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
if (erase_type == ERASE_BLOCK64K)
|
||||
sfcmd.b.cmd = p_dev->blk_erase_cmd;
|
||||
else if (erase_type == ERASE_SECTOR)
|
||||
sfcmd.b.cmd = p_dev->sec_erase_cmd;
|
||||
else
|
||||
sfcmd.b.cmd = CMD_CHIP_ERASE;
|
||||
|
||||
sfcmd.b.addrbits = (erase_type != ERASE_CHIP) ?
|
||||
SFC_ADDR_24BITS : SFC_ADDR_0BITS;
|
||||
if ((p_dev->addr_mode == ADDR_MODE_4BYTE) && (erase_type != ERASE_CHIP))
|
||||
sfcmd.b.addrbits = SFC_ADDR_32BITS;
|
||||
|
||||
snor_write_en();
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, addr, NULL);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
ret = snor_wait_busy(timeout[erase_type] * 1000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_prog_page(struct SFNOR_DEV *p_dev,
|
||||
u32 addr,
|
||||
void *p_data,
|
||||
u32 size)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
union SFCCTRL_DATA sfctrl;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = p_dev->prog_cmd;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfcmd.b.datasize = size;
|
||||
sfcmd.b.rw = SFC_WRITE;
|
||||
|
||||
sfctrl.d32 = 0;
|
||||
sfctrl.b.datalines = p_dev->prog_lines;
|
||||
sfctrl.b.enbledma = 0;
|
||||
if (p_dev->prog_cmd == CMD_PAGE_PROG_A4)
|
||||
sfctrl.b.addrlines = SFC_4BITS_LINE;
|
||||
|
||||
if (p_dev->addr_mode == ADDR_MODE_4BYTE)
|
||||
sfcmd.b.addrbits = SFC_ADDR_32BITS;
|
||||
|
||||
snor_write_en();
|
||||
|
||||
ret = sfc_request(sfcmd.d32, sfctrl.d32, addr, p_data);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
ret = snor_wait_busy(10000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_prog(struct SFNOR_DEV *p_dev, u32 addr, void *p_data, u32 size)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
u32 page_size, len;
|
||||
u8 *p_buf = (u8 *)p_data;
|
||||
|
||||
page_size = NOR_PAGE_SIZE;
|
||||
while (size) {
|
||||
len = page_size < size ? page_size : size;
|
||||
ret = snor_prog_page(p_dev, addr, p_buf, len);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
size -= len;
|
||||
addr += len;
|
||||
p_buf += len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_enable_QE(struct SFNOR_DEV *p_dev)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
int reg_index;
|
||||
int bit_offset;
|
||||
u8 status;
|
||||
|
||||
if (p_dev->manufacturer == MID_GIGADEV ||
|
||||
p_dev->manufacturer == MID_WINBOND) {
|
||||
reg_index = p_dev->QE_bits >> 3;
|
||||
bit_offset = p_dev->QE_bits & 0x7;
|
||||
ret = snor_read_status(reg_index, &status);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
|
||||
if (status & (1 << bit_offset)) /* is QE bit set */
|
||||
return SFC_OK;
|
||||
|
||||
status |= (1 << bit_offset);
|
||||
return p_dev->write_status(reg_index, status);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if (SNOR_4BIT_DATA_DETECT_EN)
|
||||
static int snor_set_dlines(struct SFNOR_DEV *p_dev, enum SFC_DATA_LINES lines)
|
||||
{
|
||||
int ret;
|
||||
u8 read_cmd[] = {CMD_FAST_READ_X1, CMD_FAST_READ_X2, CMD_FAST_READ_X4};
|
||||
|
||||
if (lines == DATA_LINES_X4) {
|
||||
ret = snor_enable_QE(p_dev);
|
||||
if (ret != SFC_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
p_dev->read_lines = lines;
|
||||
p_dev->read_cmd = read_cmd[lines];
|
||||
|
||||
if (p_dev->manufacturer == MID_GIGADEV ||
|
||||
p_dev->manufacturer == MID_WINBOND ||
|
||||
p_dev->manufacturer == MID_MACRONIX) {
|
||||
p_dev->prog_lines = (lines != DATA_LINES_X2) ?
|
||||
lines : DATA_LINES_X1;
|
||||
if (lines == DATA_LINES_X1) {
|
||||
p_dev->prog_cmd = CMD_PAGE_PROG;
|
||||
} else {
|
||||
if (p_dev->manufacturer == MID_GIGADEV ||
|
||||
p_dev->manufacturer == MID_WINBOND)
|
||||
p_dev->prog_cmd = CMD_PAGE_PROG_X4;
|
||||
else
|
||||
p_dev->prog_cmd = CMD_PAGE_PROG_A4;
|
||||
}
|
||||
}
|
||||
|
||||
return SFC_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int snor_read_data(struct SFNOR_DEV *p_dev,
|
||||
u32 addr,
|
||||
void *p_data,
|
||||
u32 size)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
union SFCCTRL_DATA sfctrl;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = p_dev->read_cmd;
|
||||
sfcmd.b.datasize = size;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
|
||||
sfctrl.d32 = 0;
|
||||
sfctrl.b.datalines = p_dev->read_lines;
|
||||
if (!(size & 0x3) && size >= 4)
|
||||
sfctrl.b.enbledma = 0;
|
||||
|
||||
if (p_dev->read_cmd == CMD_FAST_READ_X1 ||
|
||||
p_dev->read_cmd == CMD_FAST_READ_X4 ||
|
||||
p_dev->read_cmd == CMD_FAST_READ_X2 ||
|
||||
p_dev->read_cmd == CMD_FAST_4READ_X4) {
|
||||
sfcmd.b.dummybits = 8;
|
||||
} else if (p_dev->read_cmd == CMD_FAST_READ_A4) {
|
||||
sfcmd.b.addrbits = SFC_ADDR_32BITS;
|
||||
addr = (addr << 8) | 0xFF; /* Set M[7:0] = 0xFF */
|
||||
sfcmd.b.dummybits = 4;
|
||||
sfctrl.b.addrlines = SFC_4BITS_LINE;
|
||||
}
|
||||
|
||||
if (p_dev->addr_mode == ADDR_MODE_4BYTE)
|
||||
sfcmd.b.addrbits = SFC_ADDR_32BITS;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, sfctrl.d32, addr, p_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snor_read(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
u32 addr, size, len;
|
||||
u8 *p_buf = (u8 *)p_data;
|
||||
|
||||
if ((sec + n_sec) > p_dev->capacity)
|
||||
return SFC_PARAM_ERR;
|
||||
|
||||
mutex_lock(&p_dev->lock);
|
||||
addr = sec << 9;
|
||||
size = n_sec << 9;
|
||||
while (size) {
|
||||
len = size < SFC_MAX_IOSIZE ? size : SFC_MAX_IOSIZE;
|
||||
ret = snor_read_data(p_dev, addr, p_buf, len);
|
||||
if (ret != SFC_OK) {
|
||||
PRINT_SFC_E("snor_read_data %x ret= %x\n",
|
||||
addr >> 9, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
size -= len;
|
||||
addr += len;
|
||||
p_buf += len;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&p_dev->lock);
|
||||
if (!ret)
|
||||
ret = n_sec;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, const void *p_data)
|
||||
{
|
||||
int ret = SFC_OK;
|
||||
u32 len, blk_size, offset;
|
||||
u8 *p_buf = (u8 *)p_data;
|
||||
|
||||
if ((sec + n_sec) > p_dev->capacity)
|
||||
return SFC_PARAM_ERR;
|
||||
|
||||
mutex_lock(&p_dev->lock);
|
||||
while (n_sec) {
|
||||
if (sec < 512 || sec >= p_dev->capacity - 512)
|
||||
blk_size = 8;
|
||||
else
|
||||
blk_size = p_dev->blk_size;
|
||||
|
||||
offset = (sec & (blk_size - 1));
|
||||
if (!offset) {
|
||||
ret = snor_erase(p_dev, sec << 9, (blk_size == 8) ?
|
||||
ERASE_SECTOR : ERASE_BLOCK64K);
|
||||
if (ret != SFC_OK) {
|
||||
PRINT_SFC_E("snor_erase %x ret= %x\n",
|
||||
sec, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
len = (blk_size - offset) < n_sec ?
|
||||
(blk_size - offset) : n_sec;
|
||||
ret = snor_prog(p_dev, sec << 9, p_buf, len << 9);
|
||||
if (ret != SFC_OK) {
|
||||
PRINT_SFC_E("snor_prog %x ret= %x\n", sec, ret);
|
||||
goto out;
|
||||
}
|
||||
n_sec -= len;
|
||||
sec += len;
|
||||
p_buf += len << 9;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&p_dev->lock);
|
||||
if (!ret)
|
||||
ret = n_sec;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_read_id(u8 *data)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_READ_JEDECID;
|
||||
sfcmd.b.datasize = 3;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, 0, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snor_read_parameter(u32 addr, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
union SFCCMD_DATA sfcmd;
|
||||
|
||||
sfcmd.d32 = 0;
|
||||
sfcmd.b.cmd = CMD_READ_PARAMETER;
|
||||
sfcmd.b.datasize = 1;
|
||||
sfcmd.b.addrbits = SFC_ADDR_24BITS;
|
||||
sfcmd.b.dummybits = 8;
|
||||
|
||||
ret = sfc_request(sfcmd.d32, 0, addr, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 snor_get_capacity(struct SFNOR_DEV *p_dev)
|
||||
{
|
||||
return p_dev->capacity;
|
||||
}
|
||||
|
||||
static void snor_print_spi_chip_info(struct SFNOR_DEV *p_dev)
|
||||
{
|
||||
PRINT_SFC_I("addr_mode: %x\n", p_dev->addr_mode);
|
||||
PRINT_SFC_I("read_lines: %x\n", p_dev->read_lines);
|
||||
PRINT_SFC_I("prog_lines: %x\n", p_dev->prog_lines);
|
||||
PRINT_SFC_I("read_cmd: %x\n", p_dev->read_cmd);
|
||||
PRINT_SFC_I("prog_cmd: %x\n", p_dev->prog_cmd);
|
||||
PRINT_SFC_I("blk_erase_cmd: %x\n", p_dev->blk_erase_cmd);
|
||||
PRINT_SFC_I("sec_erase_cmd: %x\n", p_dev->sec_erase_cmd);
|
||||
}
|
||||
|
||||
static struct flash_info *snor_get_flash_info(u8 *flash_id)
|
||||
{
|
||||
u32 i;
|
||||
u32 id = (flash_id[0] << 16) | (flash_id[1] << 8) | (flash_id[2] << 0);
|
||||
|
||||
for (i = 0;
|
||||
i < (sizeof(spi_flash_tbl) / sizeof(struct flash_info));
|
||||
i++) {
|
||||
if (spi_flash_tbl[i].id == id)
|
||||
return &spi_flash_tbl[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adjust flash info in ram base on parameter */
|
||||
static void *snor_flash_info_adjust(struct flash_info *spi_flash_info)
|
||||
{
|
||||
u32 addr;
|
||||
u8 para_version;
|
||||
|
||||
if (spi_flash_info->id == 0xc84019) {
|
||||
addr = 0x09;
|
||||
snor_read_parameter(addr, ¶_version);
|
||||
if (para_version == 0x06) {
|
||||
spi_flash_info->QE_bits = 9;
|
||||
spi_flash_info->prog_cmd_4 = 0x34;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snor_init(struct SFNOR_DEV *p_dev)
|
||||
{
|
||||
int i;
|
||||
u8 id_byte[5];
|
||||
int err;
|
||||
|
||||
memset(p_dev, 0, sizeof(struct SFNOR_DEV));
|
||||
snor_read_id(id_byte);
|
||||
PRINT_SFC_E("sfc nor id: %x %x %x\n",
|
||||
id_byte[0], id_byte[1], id_byte[2]);
|
||||
if (0xFF == id_byte[0] || 0x00 == id_byte[0]) {
|
||||
err = SFC_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
p_dev->manufacturer = id_byte[0];
|
||||
p_dev->mem_type = id_byte[1];
|
||||
|
||||
mutex_init(&p_dev->lock);
|
||||
g_spi_flash_info = snor_get_flash_info(id_byte);
|
||||
if (g_spi_flash_info) {
|
||||
snor_flash_info_adjust(g_spi_flash_info);
|
||||
p_dev->capacity = 1 << g_spi_flash_info->density;
|
||||
p_dev->blk_size = g_spi_flash_info->block_size;
|
||||
p_dev->page_size = NOR_SECS_PAGE;
|
||||
p_dev->read_cmd = g_spi_flash_info->read_cmd;
|
||||
p_dev->prog_cmd = g_spi_flash_info->prog_cmd;
|
||||
p_dev->sec_erase_cmd = g_spi_flash_info->sector_erase_cmd;
|
||||
p_dev->blk_erase_cmd = g_spi_flash_info->block_erase_cmd;
|
||||
p_dev->prog_lines = DATA_LINES_X1;
|
||||
p_dev->read_lines = DATA_LINES_X1;
|
||||
p_dev->QE_bits = g_spi_flash_info->QE_bits;
|
||||
|
||||
i = g_spi_flash_info->feature & FEA_READ_STATUE_MASK;
|
||||
if (i == 0)
|
||||
p_dev->write_status = snor_write_status;
|
||||
else
|
||||
p_dev->write_status = snor_write_status2;
|
||||
if (g_spi_flash_info->feature & FEA_4BIT_READ) {
|
||||
if (snor_enable_QE(p_dev) == SFC_OK) {
|
||||
p_dev->read_lines = DATA_LINES_X4;
|
||||
p_dev->read_cmd = g_spi_flash_info->read_cmd_4;
|
||||
}
|
||||
}
|
||||
if ((g_spi_flash_info->feature & FEA_4BIT_PROG) &&
|
||||
(p_dev->read_lines == DATA_LINES_X4)) {
|
||||
p_dev->prog_lines = DATA_LINES_X4;
|
||||
p_dev->prog_cmd = g_spi_flash_info->prog_cmd_4;
|
||||
}
|
||||
|
||||
if (g_spi_flash_info->feature & FEA_4BYTE_ADDR)
|
||||
p_dev->addr_mode = ADDR_MODE_4BYTE;
|
||||
|
||||
if ((g_spi_flash_info->feature & FEA_4BYTE_ADDR_MODE))
|
||||
snor_enter_4byte_mode();
|
||||
|
||||
goto normal_out;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(sfnor_dev_code); i++) {
|
||||
if (id_byte[2] == sfnor_dev_code[i]) {
|
||||
p_dev->capacity = sfnor_capacity[i] >> 9;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= sizeof(sfnor_dev_code)) {
|
||||
err = SFC_ERROR;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
p_dev->QE_bits = 9;
|
||||
p_dev->blk_size = NOR_SECS_BLK;
|
||||
p_dev->page_size = NOR_SECS_PAGE;
|
||||
p_dev->read_cmd = CMD_READ_DATA;
|
||||
p_dev->prog_cmd = CMD_PAGE_PROG;
|
||||
p_dev->sec_erase_cmd = CMD_SECTOR_ERASE;
|
||||
p_dev->blk_erase_cmd = CMD_BLOCK_ERASE;
|
||||
p_dev->write_status = snor_write_status2;
|
||||
#if (SNOR_4BIT_DATA_DETECT_EN)
|
||||
snor_set_dlines(p_dev, DATA_LINES_X4);
|
||||
#endif
|
||||
|
||||
normal_out:
|
||||
snor_print_spi_chip_info(p_dev);
|
||||
|
||||
return SFC_OK;
|
||||
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
*/
|
||||
|
||||
#ifndef _SFNOR_H
|
||||
#define _SFNOR_H
|
||||
|
||||
#include "sfc.h"
|
||||
|
||||
/* Four line data transmission detection */
|
||||
#define SNOR_4BIT_DATA_DETECT_EN 0
|
||||
|
||||
#define NOR_PAGE_SIZE 256
|
||||
#define NOR_BLOCK_SIZE (64 * 1024)
|
||||
#define NOR_SECS_BLK (NOR_BLOCK_SIZE / 512)
|
||||
#define NOR_SECS_PAGE 4
|
||||
|
||||
#define FEA_READ_STATUE_MASK (0x3 << 0)
|
||||
#define FEA_STATUE_MODE1 0
|
||||
#define FEA_STATUE_MODE2 1
|
||||
#define FEA_4BIT_READ BIT(2)
|
||||
#define FEA_4BIT_PROG BIT(3)
|
||||
#define FEA_4BYTE_ADDR BIT(4)
|
||||
#define FEA_4BYTE_ADDR_MODE BIT(5)
|
||||
|
||||
/*Manufactory ID*/
|
||||
#define MID_WINBOND 0xEF
|
||||
#define MID_GIGADEV 0xC8
|
||||
#define MID_MICRON 0x2C
|
||||
#define MID_MACRONIX 0xC2
|
||||
#define MID_SPANSION 0x01
|
||||
#define MID_EON 0x1C
|
||||
#define MID_ST 0x20
|
||||
|
||||
/*Command Set*/
|
||||
#define CMD_READ_JEDECID (0x9F)
|
||||
#define CMD_READ_DATA (0x03)
|
||||
#define CMD_READ_STATUS (0x05)
|
||||
#define CMD_WRITE_STATUS (0x01)
|
||||
#define CMD_PAGE_PROG (0x02)
|
||||
#define CMD_SECTOR_ERASE (0x20)
|
||||
#define CMD_BLK64K_ERASE (0xD8)
|
||||
#define CMD_BLK32K_ERASE (0x52)
|
||||
#define CMD_CHIP_ERASE (0xC7)
|
||||
#define CMD_WRITE_EN (0x06)
|
||||
#define CMD_WRITE_DIS (0x04)
|
||||
#define CMD_PAGE_READ (0x13)
|
||||
#define CMD_GET_FEATURE (0x0F)
|
||||
#define CMD_SET_FEATURE (0x1F)
|
||||
#define CMD_PROG_LOAD (0x02)
|
||||
#define CMD_PROG_EXEC (0x10)
|
||||
#define CMD_BLOCK_ERASE (0xD8)
|
||||
#define CMD_READ_DATA_X2 (0x3B)
|
||||
#define CMD_READ_DATA_X4 (0x6B)
|
||||
#define CMD_PROG_LOAD_X4 (0x32)
|
||||
#define CMD_READ_STATUS2 (0x35)
|
||||
#define CMD_READ_STATUS3 (0x15)
|
||||
#define CMD_WRITE_STATUS2 (0x31)
|
||||
#define CMD_WRITE_STATUS3 (0x11)
|
||||
/* X1 cmd, X1 addr, X1 data */
|
||||
#define CMD_FAST_READ_X1 (0x0B)
|
||||
/* X1 cmd, X1 addr, X2 data */
|
||||
#define CMD_FAST_READ_X2 (0x3B)
|
||||
/* X1 cmd, X1 addr, X4 data SUPPORT GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_READ_X4 (0x6B)
|
||||
/* X1 cmd, X1 addr, X4 data SUPPORT GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_4READ_X4 (0x6C)
|
||||
/* X1 cmd, X4 addr, X4 data SUPPORT EON GD MARCONIX WINBOND */
|
||||
#define CMD_FAST_READ_A4 (0xEB)
|
||||
/* X1 cmd, X1 addr, X4 data, SUPPORT GD WINBOND */
|
||||
#define CMD_PAGE_PROG_X4 (0x32)
|
||||
/* X1 cmd, X4 addr, X4 data, SUPPORT MARCONIX */
|
||||
#define CMD_PAGE_PROG_A4 (0x38)
|
||||
#define CMD_RESET_NAND (0xFF)
|
||||
#define CMD_ENTER_4BYTE_MODE (0xB7)
|
||||
#define CMD_EXIT_4BYTE_MODE (0xE9)
|
||||
#define CMD_ENABLE_RESER (0x66)
|
||||
#define CMD_RESET_DEVICE (0x99)
|
||||
#define CMD_READ_PARAMETER (0x5A)
|
||||
|
||||
enum NOR_ERASE_TYPE {
|
||||
ERASE_SECTOR = 0,
|
||||
ERASE_BLOCK64K,
|
||||
ERASE_CHIP
|
||||
};
|
||||
|
||||
enum SNOR_IO_MODE {
|
||||
IO_MODE_SPI = 0,
|
||||
IO_MODE_QPI
|
||||
};
|
||||
|
||||
enum SNOR_READ_MODE {
|
||||
READ_MODE_NOMAL = 0,
|
||||
READ_MODE_FAST
|
||||
};
|
||||
|
||||
enum SNOR_ADDR_MODE {
|
||||
ADDR_MODE_3BYTE = 0,
|
||||
ADDR_MODE_4BYTE
|
||||
};
|
||||
|
||||
typedef int (*SNOR_WRITE_STATUS)(u32 reg_index, u8 status);
|
||||
|
||||
struct SFNOR_DEV {
|
||||
u32 capacity;
|
||||
u8 manufacturer;
|
||||
u8 mem_type;
|
||||
u16 page_size;
|
||||
u32 blk_size;
|
||||
|
||||
u8 read_cmd;
|
||||
u8 prog_cmd;
|
||||
u8 sec_erase_cmd;
|
||||
u8 blk_erase_cmd;
|
||||
u8 QE_bits;
|
||||
|
||||
enum SNOR_READ_MODE read_mode;
|
||||
enum SNOR_ADDR_MODE addr_mode;
|
||||
enum SNOR_IO_MODE io_mode;
|
||||
|
||||
enum SFC_DATA_LINES read_lines;
|
||||
enum SFC_DATA_LINES prog_lines;
|
||||
|
||||
SNOR_WRITE_STATUS write_status;
|
||||
struct mutex lock; /* to lock this object */
|
||||
};
|
||||
|
||||
struct flash_info {
|
||||
u32 id;
|
||||
|
||||
u8 block_size;
|
||||
u8 sector_size;
|
||||
u8 read_cmd;
|
||||
u8 prog_cmd;
|
||||
|
||||
u8 read_cmd_4;
|
||||
u8 prog_cmd_4;
|
||||
u8 sector_erase_cmd;
|
||||
u8 block_erase_cmd;
|
||||
|
||||
u8 feature;
|
||||
u8 density; /* (1 << density) sectors*/
|
||||
u8 QE_bits;
|
||||
u8 reserved2;
|
||||
};
|
||||
|
||||
int snor_init(struct SFNOR_DEV *p_dev);
|
||||
u32 snor_get_capacity(struct SFNOR_DEV *p_dev);
|
||||
int snor_read(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, void *p_data);
|
||||
int snor_write(struct SFNOR_DEV *p_dev, u32 sec, u32 n_sec, const void *p_data);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue