Merge "driver: mtd: qpic_nand: Add support for serial training."

This commit is contained in:
Linux Build Service Account 2020-05-14 12:10:48 -07:00 committed by Gerrit - the friendly Code Review server
commit aadc053ce6
3 changed files with 290 additions and 12 deletions

View file

@ -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");

View file

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

View file

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