mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2025-12-10 07:44:53 +01:00
driver: mtd: qpic_nand: Add support for serial training.
This change will add support for serial training in QPIC. Due to different PNR and PCB delays, serial read data can come with different delays to QPIC. At high frequency operations Rx clock should be adjusted according to delays so that Rx Data can be captured correctly. CLK_CNTR_INIT_VAL_VEC in NAND_FLASH_SPI_CFG register is a 12-bit vector which is divided in 4 parts of 3 bits each representing delay of 4 serial input data lines. Bit [2:0] corresponds to qspi_miso[0], bit [5:3] corresponds to qspi_miso[1], bit [8:6] corresponds to qspi_miso[2] and bit [11:9] corresponds to qspi_miso[3]. Delay of each qspi_miso line can be set from 0 to 7. For serial training the following rule should be followd. 1) SW should write a page with any known pattern in flash at lower frequency. 2) Set the CLK_CNTR_INIT_VAL_VEC for qspi_miso[0] line. 3) Read that page repetitively in high frequency mode until it gets data accurately. 4) Repeat above steps for other qspi_miso lines. Signed-off-by: Md Sadre Alam <mdalam@codeaurora.org> Change-Id: If622809efff55fb2abe60f409a590abd5313741b Signed-off-by: Md Sadre Alam <mdalam@codeaurora.org>
This commit is contained in:
parent
bca0d487d3
commit
a99d538e1b
3 changed files with 290 additions and 12 deletions
|
|
@ -504,14 +504,6 @@ void qpic_set_clk_rate(unsigned int clk_rate, int blk_type, int req_clk_src_type
|
|||
{
|
||||
switch (blk_type) {
|
||||
case QPIC_IO_MACRO_CLK:
|
||||
/* set the FB_CLK_BIT of register QPIC_QSPI_MSTR_CONFIG
|
||||
* to by pass the serial training. if this FB_CLK_BIT
|
||||
* bit enabled then , we can apply upto maximum 200MHz
|
||||
* input to IO_MACRO_BLOCK.
|
||||
*/
|
||||
writel((FB_CLK_BIT | readl(NAND_QSPI_MSTR_CONFIG)),
|
||||
NAND_QSPI_MSTR_CONFIG);
|
||||
|
||||
/* select the clk source for IO_PAD_MACRO
|
||||
* clk source wil be either XO = 24MHz. or GPLL0 = 800MHz.
|
||||
*/
|
||||
|
|
@ -532,7 +524,16 @@ void qpic_set_clk_rate(unsigned int clk_rate, int blk_type, int req_clk_src_type
|
|||
* by hardware and then that clock will be go on bus.
|
||||
* i.e 200/4MHz = 50MHz i.e 50MHz will go onto the bus.
|
||||
*/
|
||||
writel(0x107, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
if (clk_rate == IO_MACRO_CLK_320_MHZ)
|
||||
writel(0x104, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
else if (clk_rate == IO_MACRO_CLK_266_MHZ)
|
||||
writel(0x105, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
else if (clk_rate == IO_MACRO_CLK_228_MHZ)
|
||||
writel(0x106, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
else if (clk_rate == IO_MACRO_CLK_100_MHZ)
|
||||
writel(0x10F, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
else if (clk_rate == IO_MACRO_CLK_200_MHZ)
|
||||
writel(0x107, GCC_QPIC_IO_MACRO_CFG_RCGR);
|
||||
|
||||
} else {
|
||||
printf("wrong clk src selection requested.\n");
|
||||
|
|
|
|||
|
|
@ -74,8 +74,9 @@
|
|||
#define QPIC_CBCR_VAL 0x80004FF1
|
||||
#define GCC_QPIC_IO_MACRO_CMD_RCGR 0x01857010
|
||||
#define GCC_QPIC_IO_MACRO_CFG_RCGR 0x01857014
|
||||
|
||||
#define IO_MACRO_CLK_400_MHZ 400000000
|
||||
#define IO_MACRO_CLK_320_MHZ 320000000
|
||||
#define IO_MACRO_CLK_266_MHZ 266000000
|
||||
#define IO_MACRO_CLK_228_MHZ 228000000
|
||||
#define IO_MACRO_CLK_200_MHZ 200000000
|
||||
#define IO_MACRO_CLK_100_MHZ 100000000
|
||||
#define IO_MACRO_CLK_24MHZ 24000000
|
||||
|
|
@ -83,7 +84,7 @@
|
|||
#define QPIC_CORE_CLK 1
|
||||
#define XO_CLK_SRC 2
|
||||
#define GPLL0_CLK_SRC 3
|
||||
#define FB_CLK_BIT 0x10
|
||||
#define FB_CLK_BIT (1 << 4)
|
||||
#define UPDATE_EN 0x1
|
||||
|
||||
/* UART 1 */
|
||||
|
|
|
|||
|
|
@ -118,6 +118,30 @@ static struct qpic_serial_nand_params qpic_serial_nand_tbl[] = {
|
|||
struct qpic_serial_nand_params *serial_params;
|
||||
#define MICRON_DEVICE_ID 0x152c152c
|
||||
#define CMD3_MASK 0xfff0ffff
|
||||
/*
|
||||
* An array holding the fixed pattern to compare with
|
||||
* training pattern.
|
||||
*/
|
||||
static const unsigned int training_block_64[] = {
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
};
|
||||
|
||||
static const unsigned int training_block_128[] = {
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
|
||||
};
|
||||
#define TRAINING_PART_OFFSET 0x100000
|
||||
#define MAXIMUM_ALLOCATED_TRAINING_BLOCK 8
|
||||
#define TOTAL_NUM_PHASE 7
|
||||
#endif
|
||||
|
||||
struct cmd_element ce_array[100]
|
||||
|
|
@ -1129,6 +1153,7 @@ int qpic_spi_nand_config(struct mtd_info *mtd)
|
|||
uint32_t status = 0x0;
|
||||
struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd);
|
||||
uint32_t cmd3_val = NAND_FLASH_DEV_CMD3_VAL;
|
||||
|
||||
/* For micron device the READ_CACHE_SEQ command is different than
|
||||
* Giga device. for Giga 0x31 and for Micron 0x30.
|
||||
* so based on id update the command configuration register
|
||||
|
|
@ -1138,6 +1163,7 @@ int qpic_spi_nand_config(struct mtd_info *mtd)
|
|||
cmd3_val = (NAND_FLASH_DEV_CMD3_VAL & CMD3_MASK);
|
||||
writel(cmd3_val, SPI_NAND_DEV_CMD3);
|
||||
}
|
||||
|
||||
/* Get the block protection status*/
|
||||
status = qpic_serial_get_feature(mtd, FLASH_SPI_NAND_BLK_PROCT_ADDR);
|
||||
if (status < 0) {
|
||||
|
|
@ -1404,6 +1430,14 @@ static void qpic_spi_init(struct mtd_info *mtd)
|
|||
uint32_t xfer_start = NAND_XFR_STEPS_V1_5_20;
|
||||
int i;
|
||||
|
||||
/* set the FB_CLK_BIT of register QPIC_QSPI_MSTR_CONFIG
|
||||
* to by pass the serial training. if this FB_CLK_BIT
|
||||
* bit enabled then , we can apply upto maximum 200MHz
|
||||
* input to IO_MACRO_BLOCK.
|
||||
*/
|
||||
writel((FB_CLK_BIT | readl(NAND_QSPI_MSTR_CONFIG)),
|
||||
NAND_QSPI_MSTR_CONFIG);
|
||||
|
||||
qpic_set_clk_rate(IO_MACRO_CLK_200_MHZ, QPIC_IO_MACRO_CLK,
|
||||
GPLL0_CLK_SRC);
|
||||
|
||||
|
|
@ -3915,6 +3949,230 @@ qpic_nand_mtd_params(struct mtd_info *mtd)
|
|||
chip->scan_bbt = qpic_nand_scan_bbt_nop;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QSPI_SERIAL_TRAINING
|
||||
static void qpic_set_phase(int phase)
|
||||
{
|
||||
int spi_flash_cfg_val = 0x0;
|
||||
|
||||
if (phase < 1 || phase > 7) {
|
||||
printf("%s : wrong phase value\n", __func__);
|
||||
return;
|
||||
}
|
||||
/* get the current value of NAND_FLASH_SPI_CFG register */
|
||||
spi_flash_cfg_val = readl(NAND_FLASH_SPI_CFG);
|
||||
|
||||
/* set SPI_LOAD_CLK_CNTR_INIT_EN bit */
|
||||
spi_flash_cfg_val |= SPI_LOAD_CLK_CNTR_INIT_EN;
|
||||
writel(spi_flash_cfg_val, NAND_FLASH_SPI_CFG);
|
||||
|
||||
/* write the phase value for all the line */
|
||||
spi_flash_cfg_val |= ((phase << 16) | (phase << 19) |
|
||||
(phase << 22) | (phase << 25));
|
||||
writel(spi_flash_cfg_val, NAND_FLASH_SPI_CFG);
|
||||
|
||||
/* clear the SPI_LOAD_CLK_CNTR_INIT_EN bit to load the required
|
||||
* phase value
|
||||
*/
|
||||
spi_flash_cfg_val &= ~SPI_LOAD_CLK_CNTR_INIT_EN;
|
||||
writel(spi_flash_cfg_val, NAND_FLASH_SPI_CFG);
|
||||
}
|
||||
|
||||
static int qpic_find_most_appropriate_phase(u8 *phase_table, int phase_count)
|
||||
{
|
||||
int cnt;
|
||||
int phase = 0x0;
|
||||
u8 phase_ranges[TOTAL_NUM_PHASE] = {1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
/*currently we are considering continious 3 phase will
|
||||
* pass and tke the middle one out of passed three phase.
|
||||
*/
|
||||
for (cnt = 0; cnt < phase_count; cnt++) {
|
||||
if (!memcmp(phase_table+cnt, phase_ranges+cnt, 3)) {
|
||||
phase = cnt+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
phase = phase_table[phase];
|
||||
return phase;
|
||||
}
|
||||
|
||||
static int qpic_execute_serial_training(struct mtd_info *mtd)
|
||||
{
|
||||
struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd);
|
||||
struct nand_chip *chip = MTD_NAND_CHIP(mtd);
|
||||
|
||||
unsigned int start, training_offset, blk_cnt = 0;
|
||||
unsigned int offset, pageno, curr_freq;
|
||||
int size = sizeof(training_block_64);
|
||||
unsigned int io_macro_freq_tbl[] = {100000000, 200000000, 228000000,
|
||||
266000000, 320000000};
|
||||
|
||||
unsigned char *data_buff, trained_phase[TOTAL_NUM_PHASE];
|
||||
int phase, phase_cnt;
|
||||
int training_seq_cnt = 3;
|
||||
int index = 4, ret, phase_failed=0;
|
||||
|
||||
training_offset = TRAINING_PART_OFFSET;
|
||||
/* write pattern at lower frequency */
|
||||
start = (training_offset >> chip->phys_erase_shift);
|
||||
offset = (start << chip->phys_erase_shift);
|
||||
/* erase the all block */
|
||||
pageno = (offset >> chip->page_shift);
|
||||
|
||||
/* At 50Mhz frequency check the bad blocks, if training
|
||||
* blocks is not bad then only start training else operate
|
||||
* at 50Mhz with bypassing software serial traning.
|
||||
*/
|
||||
while (qpic_nand_block_isbad(mtd, offset) != 0) {
|
||||
/* block is bad skip this block and goto next
|
||||
* block
|
||||
*/
|
||||
training_offset += mtd->erasesize;
|
||||
start = (training_offset >> chip->phys_erase_shift);
|
||||
offset = (start << chip->phys_erase_shift);
|
||||
pageno = (offset >> chip->page_shift);
|
||||
blk_cnt++;
|
||||
}
|
||||
|
||||
if (blk_cnt == MAXIMUM_ALLOCATED_TRAINING_BLOCK) {
|
||||
printf("All training blocks are bad skipping serial training\n");
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = qpic_nand_blk_erase(mtd, pageno);
|
||||
if (ret) {
|
||||
printf("error in erasing training block @%x\n",offset);
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
data_buff = (unsigned char *)malloc(size);
|
||||
if (!data_buff) {
|
||||
printf("Errorn in allocating memory.\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
memset(data_buff, 0, size);
|
||||
memcpy(data_buff, training_block_64, size);
|
||||
|
||||
/*write training data to flash */
|
||||
ret = NANDC_RESULT_SUCCESS;
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
/* write this dumy byte in spare area to avoid bam
|
||||
* transaction error while writing.
|
||||
*/
|
||||
memset(dev->pad_oob, 0xFF, dev->oob_per_page);
|
||||
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
ops.len = size;
|
||||
ops.retlen = 0;
|
||||
ops.ooblen = dev->oob_per_page;
|
||||
ops.oobretlen = 0;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = (uint8_t *)data_buff;
|
||||
ops.oobbuf = (uint8_t *)dev->pad_oob;
|
||||
|
||||
/* write should be only once */
|
||||
ret = qpic_nand_write_page(mtd, pageno, NAND_CFG, &ops);
|
||||
if (ret) {
|
||||
printf("Error in writing training data..\n");
|
||||
goto err;
|
||||
}
|
||||
/* After write verify the the data with read @ lower frequency
|
||||
* after that only start serial tarining @ higher frequency
|
||||
*/
|
||||
memset(data_buff, 0, size);
|
||||
ops.datbuf = (uint8_t *)data_buff;
|
||||
|
||||
ret = qpic_nand_read_page(mtd, pageno, NAND_CFG, &ops);
|
||||
if (ret) {
|
||||
printf("%s : Read training data failed.\n",__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* compare original data and read data */
|
||||
if (memcmp(data_buff, training_block_64, size)) {
|
||||
printf("Training data read failed @ lower frequency\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* disable feed back clock bit to start serial training */
|
||||
writel((~FB_CLK_BIT & readl(NAND_QSPI_MSTR_CONFIG)),
|
||||
NAND_QSPI_MSTR_CONFIG);
|
||||
|
||||
/* start serial training here */
|
||||
curr_freq = io_macro_freq_tbl[index];
|
||||
rettry:
|
||||
phase = 1;
|
||||
phase_cnt = 0;
|
||||
|
||||
/* set frequency, start from higer frequency */
|
||||
qpic_set_clk_rate(curr_freq, QPIC_IO_MACRO_CLK, GPLL0_CLK_SRC);
|
||||
|
||||
do {
|
||||
/* set the phase */
|
||||
qpic_set_phase(phase);
|
||||
|
||||
memset(data_buff, 0, size);
|
||||
ops.datbuf = (uint8_t *)data_buff;
|
||||
|
||||
ret = qpic_nand_read_page(mtd, pageno, NAND_CFG, &ops);
|
||||
if (ret) {
|
||||
printf("%s : Read training data failed.\n",__func__);
|
||||
goto err;
|
||||
}
|
||||
/* compare original data and read data */
|
||||
if (memcmp(data_buff, training_block_64, size)) {
|
||||
/* wrong data read on one of miso line
|
||||
* change the phase value and try again
|
||||
*/
|
||||
continue;
|
||||
phase_failed++;
|
||||
} else {
|
||||
/* we got good phase update the good phase list
|
||||
*/
|
||||
trained_phase[phase_cnt++] = phase;
|
||||
/*printf("%s : Found good phase %d\n",__func__,phase);*/
|
||||
}
|
||||
|
||||
} while (phase++ <= TOTAL_NUM_PHASE);
|
||||
|
||||
if (phase_cnt) {
|
||||
/* Get the appropriate phase */
|
||||
phase = qpic_find_most_appropriate_phase(trained_phase, phase_cnt);
|
||||
qpic_set_phase(phase);
|
||||
} else {
|
||||
/* lower the the clock frequency
|
||||
* and try again
|
||||
*/
|
||||
curr_freq = io_macro_freq_tbl[--index];
|
||||
if (--training_seq_cnt)
|
||||
goto rettry;
|
||||
|
||||
/* Training failed */
|
||||
printf("%s : Serial training failed\n",__func__);
|
||||
ret = -EIO;
|
||||
goto free;
|
||||
}
|
||||
|
||||
/* if phase_failed == 7 it means serial traing failed
|
||||
* on all the phase. so now we have to go via line by line
|
||||
* i.e first check for MISO_0, with all the phase value i.e
|
||||
* 1-7 and then MISO_1 and so on.
|
||||
* NOTE: But this is the worse case , and it this type of senario
|
||||
* will not come. if it will come then go with this design.
|
||||
* ======To DO=====
|
||||
*/
|
||||
free:
|
||||
free(data_buff);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
|
||||
|
||||
void qpic_nand_init(qpic_nand_cfg_t *qpic_nand_cfg)
|
||||
|
|
@ -4103,6 +4361,24 @@ void qpic_nand_init(qpic_nand_cfg_t *qpic_nand_cfg)
|
|||
dev->tmp_oobbuf = buf;
|
||||
buf += mtd->oobsize;
|
||||
|
||||
#ifdef CONFIG_QSPI_SERIAL_TRAINING
|
||||
/* start serial training here */
|
||||
ret = qpic_execute_serial_training(mtd);
|
||||
if (ret) {
|
||||
printf("Error in serial training.\n");
|
||||
printf("switch back to 50MHz with feed back clock bit enabled\n");
|
||||
writel((FB_CLK_BIT | readl(NAND_QSPI_MSTR_CONFIG)),
|
||||
NAND_QSPI_MSTR_CONFIG);
|
||||
|
||||
qpic_set_clk_rate(IO_MACRO_CLK_200_MHZ, QPIC_IO_MACRO_CLK,
|
||||
GPLL0_CLK_SRC);
|
||||
|
||||
writel(0x0, NAND_FLASH_SPI_CFG);
|
||||
writel(SPI_CFG_VAL, NAND_FLASH_SPI_CFG);
|
||||
writel((SPI_CFG_VAL & ~SPI_LOAD_CLK_CNTR_INIT_EN),
|
||||
NAND_FLASH_SPI_CFG);
|
||||
}
|
||||
#endif
|
||||
/* Register with MTD subsystem. */
|
||||
ret = nand_register(CONFIG_QPIC_NAND_NAND_INFO_IDX);
|
||||
if (ret < 0) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue