summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2012-09-04 09:44:18 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2012-09-04 09:48:48 +0200
commit072badeafbf33b49cba38d4d837110a7766b5c8e (patch)
tree2b675d18a1393e115a422f58a9162b7c3bae8b42 /firmware
parenta1b101b107900e0d163700fda0aab0efc429f55e (diff)
rk27xx: Put some nand handling functions for reference
The functions document my reverse engineer findings about nand controller. This code is commented out and is purely for reference as FTL scheme is still unknown. Change-Id: I70edeb4bfb0cbd51b6adc15afa7193dd8f71e8da
Diffstat (limited to 'firmware')
-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);