mirror of
https://git.codelinaro.org/clo/qsdk/oss/boot/u-boot-2016.git
synced 2026-02-14 20:09:24 +01:00
Merge "driver: mtd: qpic_nand: Add support for serial training."
This commit is contained in:
commit
aadc053ce6
3 changed files with 290 additions and 12 deletions
|
|
@ -511,14 +511,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.
|
||||
*/
|
||||
|
|
@ -539,7 +531,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