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:
Dingqiang Lin 2018-05-08 21:32:34 +08:00 committed by Kever Yang
parent 31c3ca3223
commit ad309a883b
15 changed files with 1749 additions and 0 deletions

View File

@ -76,6 +76,8 @@ source "drivers/reset/Kconfig"
source "drivers/rknand/Kconfig"
source "drivers/rkflash/Kconfig"
source "drivers/rtc/Kconfig"
source "drivers/scsi/Kconfig"

View File

@ -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/

32
drivers/rkflash/Kconfig Normal file
View File

@ -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

8
drivers/rkflash/Makefile Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -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__ */

View File

@ -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
}

View File

@ -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

View File

@ -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,
};

195
drivers/rkflash/sfc.c Normal file
View File

@ -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;
}

177
drivers/rkflash/sfc.h Normal file
View File

@ -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

612
drivers/rkflash/sfc_nor.c Normal file
View File

@ -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, &para_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;
}

154
drivers/rkflash/sfc_nor.h Normal file
View File

@ -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