summaryrefslogtreecommitdiff
path: root/firmware/target/arm/rk27xx/nand-rk27xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/rk27xx/nand-rk27xx.c')
-rw-r--r--firmware/target/arm/rk27xx/nand-rk27xx.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/nand-rk27xx.c b/firmware/target/arm/rk27xx/nand-rk27xx.c
index c2c855efee..84e60a47e1 100644
--- a/firmware/target/arm/rk27xx/nand-rk27xx.c
+++ b/firmware/target/arm/rk27xx/nand-rk27xx.c
@@ -22,6 +22,400 @@
#include "config.h"
#include "nand-target.h"
+#if 0
+/* This is for documentation purpose as FTL has not been reverse engineered yet
+ * Raw nand handling functions based on OF disassembly and partially inspired
+ * by Rockchip patent
+ */
+
+#define MAX_FLASH_NUM 4
+#define CMD_READ_STATUS 0x70
+#define CMD_RESET 0xFF
+#define CMD_READ_ID 0x90
+#define READ_PAGE_CMD 0x30
+
+/* this is the struct OF uses */
+struct flashspec_t
+{
+ uint8_t cache_prog;
+ uint8_t mul_plane;
+ uint8_t interleave;
+ uint8_t large;
+ uint8_t five;
+ uint8_t mlc;
+ uint8_t vendor;
+ uint8_t access_time;
+ uint8_t sec_per_page;
+ uint8_t sec_per_page_raw;
+ uint16_t sec_per_block;
+ uint16_t sec_per_block_raw;
+ uint16_t page_per_block;
+ uint16_t page_per_block_raw;
+
+ uint32_t tot_logic_sec;
+ uint32_t total_phy_sec;
+ uint32_t total_bloks;
+
+ uint32_t cmd;
+ uint32_t addr;
+ uint32_t data;
+};
+
+/* holds nand chips characteristics */
+struct flashspec_t flash_spec[MAX_FLASH_NUM];
+
+/* sum of all phy sectors in all chips */
+uint32_t total_phy_sec;
+
+enum vendor_t {
+ SAMSUNG,
+ TOSHIBA,
+ HYNIX,
+ INFINEON,
+ MICRON,
+ RENESAS,
+ ST
+};
+
+/* taken from OF */
+const uint8_t device_code[] = {
+ 0x76,
+ 0x79,
+ 0xf1,
+ 0xda,
+ 0xdc,
+ 0xd3,
+ 0xd7
+};
+
+const uint8_t manufacture_id_tbl[] =
+{
+ 0xec, /* SAMSUNG */
+ 0x98, /* TOSHIBA */
+ 0xad, /* HYNIX */
+ 0xc1, /* INFINEON */
+ 0x2c, /* MICRON */
+ 0x07, /* RENESAS */
+ 0x20 /* ST */
+};
+
+/* phisical sectors */
+const uint32_t device_info[] =
+{
+ 0x20000, /* 64M, small page */
+ 0x40000, /* 128M, small page */
+ 0x40000, /* 128M, large page */
+ 0x80000, /* 256M, large page */
+ 0x100000, /* 512M, large page */
+ 0x200000, /* 1G, large page */
+ 0x400000, /* 2G, large page */
+ 0x800000 /* 4G, large page */
+};
+
+static int flash_delay(int n)
+{
+ volatile int cnt, i, j;
+
+ for (j=0; j<n; j++)
+ for (i=0; i<10000; i++)
+ cnt++;
+
+ return cnt;
+}
+
+
+static void flash_wait_busy(void)
+{
+ unsigned int i;
+
+ for (i=0; i<0x2000; i++)
+ {
+ if (FMCTL & FM_RDY)
+ break;
+
+ flash_delay(1);
+ }
+}
+
+void flash_chip_deselect(void)
+{
+ uint32_t tmp;
+ tmp = FMCTL;
+ tmp &= 0xf0;
+ FMCTL = tmp;
+}
+
+void flash_chip_select(uint8_t chip)
+{
+ uint32_t tmp;
+
+ /* Maybe we should handle IOMUX here as well?
+ * for chip == 0,1 it is not needed
+ */
+ tmp = FMCTL;
+ tmp &= 0xf0;
+ tmp |= 1<<chip;
+ FMCTL = tmp;
+}
+
+void flash_init(void)
+{
+ uint8_t buff[5]; /* buff for CMD_READ_ID response */
+ uint32_t i,j;
+
+ mlc_refresh_row = 0xffffffff;
+ total_phy_sec = 0;
+ flash_pend_cmd.valid = 0;
+ flash_read_status_cmd = CMD_READ_STATUS;
+
+ FMWAIT = 0x1081;
+ FLCTL = FL_RST;
+
+ for (i=0; i< MAX_FLASH_NUM; i++)
+ {
+ /* Redundat - we will use special macros
+ * just for reference what OF does
+ */
+ flash_spec[i].cmd = 0x180E8200 + (i<<9);
+ flash_spec[i].addr = 0x180E204 + (i<<9);
+ flash_spec[i].data = 0x180E208 + (i<<9);
+
+ flash_chip_select(i);
+ FLASH_CMD(i) = CMD_RESET; /* write cmd to flash chip */
+ flash_delay(2);
+ flash_wait_busy();
+ FLASH_CMD(i) = CMD_READ_ID; /* write cmd to flash chip */
+ FLASH_ADDR(i) = 0x00;
+
+ /* read 5 bytes of CMD_READ_ID response */
+ for (j=0; j<5; j++)
+ buff[j] = FLASH_DATA(i);
+
+ flash_chip_deselect();
+
+ /* Get the vendor of the chip */
+ for (j=0; j<sizeof(manufacture_id_tbl); j++)
+ {
+ /* store Manufacturer index */
+ if (ManufactureIDTbl[j] == buff[0])
+ {
+ flash_spec[i].vendor = j;
+ }
+ }
+
+ for (j=0; j<sizeof(device_code); j++)
+ {
+ /* look for matching device code
+ * and store total phys sectors
+ */
+ if (DeviceCode[j] == buff[1])
+ {
+ flash_spec[i].total_phy_sec = device_info[j];
+ break;
+ }
+ }
+
+ /* div zero is fatal for us (not for OF :P) */
+ if (flash_spec[i].total_phy_sec == 0)
+ continue;
+
+ /* loc_7e8 */
+ flash_spec[i].mlc = 0;
+ flash_spec[i].large = 0;
+ flash_spec[i].five = 1;
+ flash_spec[i].mul_plane = 1;
+ flash_spec[i].interleave = 0;
+
+ flash_spec[i].cache_prog = buff[2] & 0x80;
+
+ /* flash access time (ns) */
+ switch (buff[3] & 0x88)
+ {
+ case 0:
+ flash_spec[i].access_time = 50;
+ break;
+ case 0x80:
+ flash_spec[i].access_time = 25;
+ break;
+ case 0x08:
+ flash_spec[i].access_time = 20;
+ break;
+ default:
+ flash_spec[i].access_time = 60;
+ }
+
+ /* set_large
+ * j is index in device_code table
+ */
+ if (j < 2)
+ {
+ /* small block */
+ flash_spec[i].large = 0;
+ flash_spec[i].sec_per_page_raw = 1;
+ flash_spec[i].sec_per_block_raw = 32;
+ }
+ else
+ {
+ flash_spec[i].large = 1;
+ if (j == 2)
+ flash_spec[i].five = 0;
+
+
+ /* cell type */
+ flash_spec[i].mlc = (buff[2] >> 2) & 0x03;
+
+ flash_spec[i].sec_per_page_raw = 2; /* 1KB~8KB */
+
+ /* set_sec_per_page_raw */
+ flash_spec[i].sec_per_page_raw <<= (buff[3] & 3);
+
+ flash_spec[i].sec_per_block_raw = 128; /* 64KB~512KB */
+
+ /* set_sec_per_block_raw */
+ flash_spec[i].sec_per_block_raw <<= ((buff[3]>>4) & 3);
+
+ /* simult_prog */
+ if (buff[2] & 0x30)
+ {
+ /* buff4_mulplane */
+ flash_spec[i].mul_plane <<= ((buff[4]>>2) & 3);
+ }
+
+ /* set_interleave */
+ if (flash_spec[i].vendor == TOSHIBA)
+ {
+ flash_spec[i].mul_plane = 2;
+ if (buff[2] & 3)
+ flash_spec[i].interleave = 1;
+ }
+
+ } /* large block */
+
+ if (flash_spec[i].mul_plane > 2)
+ {
+ flash_spec[i].mul_plane = 2;
+ flash_spec[i].interleave = 1;
+ }
+
+ flash_spec[i].page_per_block_raw = flash_spec[i].sec_per_block_raw/flash_spec[i].sec_per_page_raw;
+ flash_spec[i].page_per_block = flash_spec[i].page_per_block_raw * flash_spec[i].mul_plane;
+ flash_spec[i].sec_per_block = flash_spec[i].sec_per_block_raw * flash_spec[i].mul_plane;
+ flash_spec[i].sec_per_page = flash_spec[i].sec_per_page_raw * flash_spec[i].mul_plane;
+ flash_spec[i].total_bloks = flash_spec[i].total_phy_sec / flash_spec[i].sec_per_block;
+
+ total_phy_sec += flash_spec[i].total_phy_sec;
+ }
+
+ /* read ID block and propagate SysDiskCapacity and SysResBlocks */
+}
+
+/* read single page in unbuffered mode */
+void flash_read_page(int page, unsigned char *pgbuff)
+{
+ unsigned int i;
+
+ flash_chip_select(0);
+ flash_delay(2);
+ flash_wait_busy();
+
+ /* setup transfer */
+ FLASH_CMD(0) = 0x00;
+ FLASH_ADDR(0) = 0x00; /* column */
+ FLASH_ADDR(0) = 0x00; /* column */
+ FLASH_ADDR(0) = page & 0xff; /* row */
+ FLASH_ADDR(0) = (page >> 8) & 0xff; /* row */
+ FLASH_ADDR(0) = (page >> 16) & 0xff; /* row */
+ FLASH_CMD(0) = READ_PAGE_CMD;
+
+ /* wait for operation complete */
+ flash_wait_busy();
+
+ /* copy data from page register
+ * WARNING flash page size can be different
+ * for different chips. This value should be set
+ * based on initialization.
+ */
+ for (i=0; i<(4096+218); i++)
+ pgbuff[i] = FLASH_DATA(0);
+
+ flash_chip_deselect();
+}
+
+void flash_read_sector(int page, unsigned char *secbuf, int nsec)
+{
+ int i = 0;
+ int j = 0;
+
+ /* WARNING this code assumes only one nand chip
+ * it does not handle data split across different nand chips
+ */
+ flash_chip_select(0);
+ flash_delay(2);
+ flash_wait_busy();
+
+ FLASH_CMD(0) = 0x00;
+ FLASH_ADDR(0) = 0x00;
+ FLASH_ADDR(0) = 0x00;
+ FLASH_ADDR(0) = page & 0xff;
+ FLASH_ADDR(0) = (page >> 8) & 0xff;
+ FLASH_ADDR(0) = (page >> 16) & 0xff;
+ FLASH_CMD(0) = READ_PAGE_CMD;
+
+ flash_delay(1);
+
+ /* wait for operation to complete */
+ flash_wait_busy();
+
+ /* enables hw checksum control most probably */
+ BCHCTL = 1;
+
+ /* This initializes the transfer from the nand to the buffer
+ * There are 4 consecutive hw buffers 512 bytes long for data (PAGE_BUF)
+ * and 4 16 bytes long for metadata (BCH code checksum) (SPARE_BUF)
+ */
+ FLCTL = 0xA24;
+
+ /* This scheme utilizes some overlap in data transfers -
+ * data are copied from buffer to the mem and from nand to the buf
+ * at the same time.
+ */
+ while (++j < nsec)
+ {
+ /* wait for transfer to complete */
+ while(! (FLCTL & FL_RDY));
+
+ /* initialize next transfer to the next buffer */
+ FLCTL = 0xA24 | (j&3)<<3;
+
+ /* copy data chunk */
+ memcpy(secbuf, (((unsigned char *)&PAGE_BUF)+((i&3)<<9)), 0x200);
+ secbuf += 0x200;
+
+ /* copy metadata chunk (BCH)
+ * in real application this can be discarded
+ */
+ memcpy(secbuf, (((unsigned char *)&SPARE_BUF)+((i&3)<<4)), 0x10);
+ secbuf += 0x10;
+ i++;
+ }
+
+ /* wait for transfer to complete */
+ while(! (FLCTL & FL_RDY));
+
+ /* copy data chunk */
+ memcpy(secbuf, (((unsigned char *)&PAGE_BUF)+((i&3)<<9)), 0x200);
+ secbuf += 0x200;
+
+ /* copy metadata chunk (BCH)
+ * in real application this can be discarded
+ */
+ memcpy(secbuf, (((unsigned char *)&SPARE_BUF)+((i&3)<<4)), 0x10);
+ secbuf += 0x10;
+
+ flash_chip_deselect();
+}
+
+#endif
const struct nand_device_info_type* nand_get_device_type(uint32_t bank);