6887 lines
196 KiB
C
6887 lines
196 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (c) 2019 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/io.h>
|
||
|
|
||
|
#include <linux/time.h>
|
||
|
#include <linux/delay.h>
|
||
|
|
||
|
#include <linux/mmc/card.h>
|
||
|
#include <linux/mmc/host.h>
|
||
|
#include <linux/mmc/mmc.h>
|
||
|
#include <linux/mmc/sdio.h>
|
||
|
|
||
|
#include <linux/completion.h>
|
||
|
#include <linux/scatterlist.h>
|
||
|
|
||
|
#include "autok.h"
|
||
|
#include "mtk_sd.h"
|
||
|
#include "autok_cust.h"
|
||
|
#include "mmc/core/card.h"
|
||
|
|
||
|
/* 100ms */
|
||
|
#define AUTOK_CMD_TIMEOUT (HZ / 10)
|
||
|
/* 1s x 3 */
|
||
|
#define AUTOK_DAT_TIMEOUT (HZ * 3)
|
||
|
#define MSDC_FIFO_THD_1K (1024)
|
||
|
#define TUNE_TX_CNT (10)
|
||
|
#define CHECK_QSR (0x800D)
|
||
|
#define TUNE_DATA_TX_ADDR (0x358000)
|
||
|
#define CMDQ
|
||
|
#define AUTOK_CMD_TIMES (20)
|
||
|
/* scan result may find xxxxooxxx */
|
||
|
#define AUTOK_TUNING_INACCURACY (10)
|
||
|
#define AUTOK_MARGIN_THOLD (5)
|
||
|
#define AUTOK_BD_WIDTH_REF (3)
|
||
|
|
||
|
#define AUTOK_READ 0
|
||
|
#define AUTOK_WRITE 1
|
||
|
|
||
|
#define AUTOK_FINAL_CKGEN_SEL (0)
|
||
|
#define SCALE_TA_CNTR (8)
|
||
|
#define SCALE_CMD_RSP_TA_CNTR (8)
|
||
|
#define SCALE_WDAT_CRC_TA_CNTR (8)
|
||
|
#define SCALE_INT_DAT_LATCH_CK_SEL (8)
|
||
|
#define SCALE_INTERNAL_DLY_CNTR (32)
|
||
|
#define SCALE_PAD_DAT_DLY_CNTR (32)
|
||
|
|
||
|
#define TUNING_INACCURACY (2)
|
||
|
|
||
|
enum TUNE_TYPE {
|
||
|
TUNE_CMD = 0,
|
||
|
TUNE_DATA,
|
||
|
TUNE_LATCH_CK,
|
||
|
TUNE_SDIO_PLUS,
|
||
|
};
|
||
|
|
||
|
#define autok_msdc_retry(expr, retry, cnt) \
|
||
|
do { \
|
||
|
int backup = cnt; \
|
||
|
while (retry) { \
|
||
|
if (!(expr)) \
|
||
|
break; \
|
||
|
if (cnt-- == 0) { \
|
||
|
retry--; cnt = backup; \
|
||
|
} \
|
||
|
} \
|
||
|
WARN_ON(retry == 0); \
|
||
|
} while (0)
|
||
|
|
||
|
#define autok_msdc_reset() \
|
||
|
do { \
|
||
|
int retry = 3, cnt = 1000; \
|
||
|
MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_RST); \
|
||
|
/* ensure reset operation be sequential */ \
|
||
|
mb(); \
|
||
|
autok_msdc_retry(MSDC_READ32(MSDC_CFG) & MSDC_CFG_RST, \
|
||
|
retry, cnt); \
|
||
|
} while (0)
|
||
|
|
||
|
#define msdc_rxfifocnt() \
|
||
|
((MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0)
|
||
|
#define msdc_txfifocnt() \
|
||
|
((MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16)
|
||
|
|
||
|
#define wait_cond_tmo(cond, tmo) \
|
||
|
do { \
|
||
|
unsigned long timeout = jiffies + tmo; \
|
||
|
while (1) { \
|
||
|
if ((cond) || (tmo == 0)) \
|
||
|
break; \
|
||
|
if (time_after(jiffies, timeout) && (!cond)) \
|
||
|
tmo = 0; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
#define msdc_clear_fifo() \
|
||
|
do { \
|
||
|
int retry = 5, cnt = 1000; \
|
||
|
MSDC_SET_BIT32(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \
|
||
|
/* ensure fifo clear operation be sequential */ \
|
||
|
mb(); \
|
||
|
autok_msdc_retry(MSDC_READ32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, \
|
||
|
retry, cnt); \
|
||
|
} while (0)
|
||
|
|
||
|
struct AUTOK_PARAM_RANGE {
|
||
|
unsigned int start;
|
||
|
unsigned int end;
|
||
|
};
|
||
|
|
||
|
struct AUTOK_PARAM_INFO {
|
||
|
struct AUTOK_PARAM_RANGE range;
|
||
|
char *param_name;
|
||
|
};
|
||
|
|
||
|
struct BOUND_INFO {
|
||
|
unsigned int Bound_Start;
|
||
|
unsigned int Bound_End;
|
||
|
unsigned int Bound_width;
|
||
|
bool is_fullbound;
|
||
|
};
|
||
|
|
||
|
/* Max Allowed Boundary Number */
|
||
|
#define BD_MAX_CNT 4
|
||
|
struct AUTOK_SCAN_RES {
|
||
|
/* Bound info record, currently only allow max to 2 bounds exist,
|
||
|
* but in extreme case, may have 4 bounds
|
||
|
*/
|
||
|
struct BOUND_INFO bd_info[BD_MAX_CNT];
|
||
|
/* Bound cnt record, must be in rang [0,3] */
|
||
|
unsigned int bd_cnt;
|
||
|
/* Full boundary cnt record */
|
||
|
unsigned int fbd_cnt;
|
||
|
};
|
||
|
|
||
|
struct AUTOK_REF_INFO {
|
||
|
/* inf[0] - rising edge res, inf[1] - falling edge res */
|
||
|
struct AUTOK_SCAN_RES scan_info[2];
|
||
|
/* optimised sample edge select */
|
||
|
unsigned int opt_edge_sel;
|
||
|
/* optimised dly cnt sel */
|
||
|
unsigned int opt_dly_cnt;
|
||
|
/* 1clk cycle equal how many delay cell cnt, if cycle_cnt is 0,
|
||
|
* that is cannot calc cycle_cnt by current Boundary info
|
||
|
*/
|
||
|
unsigned int cycle_cnt;
|
||
|
};
|
||
|
|
||
|
struct BOUND_INFO_NEW {
|
||
|
/* boundary start and end position */
|
||
|
unsigned char bd_s;
|
||
|
unsigned char bd_e;
|
||
|
};
|
||
|
|
||
|
/* Max Allowed Boundary Number */
|
||
|
#define BD_MAX_CNT_NEW 32
|
||
|
struct AUTOK_SCAN_RES_NEW {
|
||
|
/* bd info record, currently only allow max to 32 fail bounds exist */
|
||
|
struct BOUND_INFO_NEW fail_info[BD_MAX_CNT_NEW];
|
||
|
struct BOUND_INFO_NEW pass_info[BD_MAX_CNT_NEW];
|
||
|
/* bd cnt record */
|
||
|
unsigned char fail_cnt;
|
||
|
unsigned char pass_cnt;
|
||
|
};
|
||
|
struct AUTOK_REF_INFO_NEW {
|
||
|
/* inf[0] - rising edge res, inf[1] - falling edge res */
|
||
|
struct AUTOK_SCAN_RES_NEW scan_info[2];
|
||
|
/* optimised sample edge select */
|
||
|
unsigned int opt_edge_sel;
|
||
|
/* optimised dly cnt sel */
|
||
|
unsigned int opt_dly_cnt;
|
||
|
/* 1clk cycle equal how many delay cell cnt, if cycle_cnt is 0,
|
||
|
* that is cannot calc cycle_cnt by current Boundary info
|
||
|
*/
|
||
|
unsigned int cycle_cnt;
|
||
|
};
|
||
|
|
||
|
enum AUTOK_TX_SCAN_STA_E {
|
||
|
START_POSITION = 0,
|
||
|
PASS_POSITION,
|
||
|
FAIL_POSITION,
|
||
|
};
|
||
|
|
||
|
enum AUTOK_SCAN_WIN {
|
||
|
CMD_RISE,
|
||
|
CMD_FALL,
|
||
|
DAT_RISE,
|
||
|
DAT_FALL,
|
||
|
DS_CMD_WIN,
|
||
|
DS_DATA_WIN,
|
||
|
D_CMD_RX,
|
||
|
D_DATA_RX,
|
||
|
H_CMD_TX,
|
||
|
H_DATA_TX,
|
||
|
};
|
||
|
|
||
|
enum EXD_RW_FLAG {
|
||
|
EXT_READ = 0,
|
||
|
EXT_WRITE,
|
||
|
};
|
||
|
|
||
|
struct autok_host {
|
||
|
u32 clk_tx;
|
||
|
u32 fifo_tune;
|
||
|
};
|
||
|
|
||
|
unsigned int autok_debug_level = AUTOK_DBG_RES;
|
||
|
|
||
|
const struct AUTOK_PARAM_INFO autok_param_info[] = {
|
||
|
{{0, 1}, "CMD_EDGE"},
|
||
|
{{0, 1}, "CMD_FIFO_EDGE"},
|
||
|
/* async fifo mode Pad dat edge must fix to 0 */
|
||
|
{{0, 1}, "RDATA_EDGE"},
|
||
|
{{0, 1}, "RD_FIFO_EDGE"},
|
||
|
{{0, 1}, "WD_FIFO_EDGE"},
|
||
|
|
||
|
/* Cmd Pad Tune Data Phase */
|
||
|
{{0, 31}, "CMD_RD_D_DLY1"},
|
||
|
{{0, 1}, "CMD_RD_D_DLY1_SEL"},
|
||
|
{{0, 31}, "CMD_RD_D_DLY2"},
|
||
|
{{0, 1}, "CMD_RD_D_DLY2_SEL"},
|
||
|
|
||
|
/* Data Pad Tune Data Phase */
|
||
|
{{0, 31}, "DAT_RD_D_DLY1"},
|
||
|
{{0, 1}, "DAT_RD_D_DLY1_SEL"},
|
||
|
{{0, 31}, "DAT_RD_D_DLY2"},
|
||
|
{{0, 1}, "DAT_RD_D_DLY2_SEL"},
|
||
|
|
||
|
/* Latch CK for data read when clock stop */
|
||
|
{{0, 7}, "INT_DAT_LATCH_CK"},
|
||
|
|
||
|
/* eMMC50 Related tuning param */
|
||
|
{{0, 31}, "EMMC50_DS_Z_DLY1"},
|
||
|
{{0, 1}, "EMMC50_DS_Z_DLY1_SEL"},
|
||
|
{{0, 31}, "EMMC50_DS_Z_DLY2"},
|
||
|
{{0, 1}, "EMMC50_DS_Z_DLY2_SEL"},
|
||
|
{{0, 31}, "EMMC50_DS_ZDLY_DLY"},
|
||
|
{{0, 31}, "EMMC50_CMD_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA0_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA1_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA2_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA3_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA4_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA5_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA6_TX_DLY"},
|
||
|
{{0, 31}, "EMMC50_DATA7_TX_DLY"},
|
||
|
/* tx clk dly fix to 0 for HQA res */
|
||
|
{{0, 31}, "PAD_CLK_TXDLY_AUTOK"},
|
||
|
|
||
|
/* ================================================= */
|
||
|
/* Timming Related Mux & Common Setting Config */
|
||
|
/* all data line path share sample edge */
|
||
|
{{0, 1}, "READ_DATA_SMPL_SEL"},
|
||
|
{{0, 1}, "WRITE_DATA_SMPL_SEL"},
|
||
|
/* clK tune all data Line share dly */
|
||
|
{{0, 1}, "DATA_DLYLINE_SEL"},
|
||
|
/* data tune mode select */
|
||
|
{{0, 1}, "MSDC_WCRC_ASYNC_FIFO_SEL"},
|
||
|
/* data tune mode select */
|
||
|
{{0, 1}, "MSDC_RESP_ASYNC_FIFO_SEL"},
|
||
|
/* eMMC50 Function Mux */
|
||
|
/* write path switch to emmc45 */
|
||
|
{{0, 1}, "EMMC50_WDATA_MUX_EN"},
|
||
|
/* response path switch to emmc45 */
|
||
|
{{0, 1}, "EMMC50_CMD_MUX_EN"},
|
||
|
{{0, 1}, "EMMC50_CMD_RESP_LATCH"},
|
||
|
{{0, 1}, "EMMC50_WDATA_EDGE"},
|
||
|
/* Common Setting Config */
|
||
|
{{0, 31}, "CKGEN_MSDC_DLY_SEL"},
|
||
|
{{1, 7}, "CMD_RSP_TA_CNTR"},
|
||
|
{{1, 7}, "WRDAT_CRCS_TA_CNTR"},
|
||
|
{{0, 1}, "SDC_RX_ENHANCE"},
|
||
|
};
|
||
|
|
||
|
/**********************************************************
|
||
|
* AutoK Basic Interface Implenment *
|
||
|
**********************************************************/
|
||
|
static int autok_sdio_device_rx_set(struct msdc_host *host,
|
||
|
unsigned int func_num, unsigned int base_addr,
|
||
|
unsigned int *reg_value, unsigned int r_w_dirc,
|
||
|
unsigned int opcode)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int rawcmd = 0;
|
||
|
unsigned int arg = 0;
|
||
|
unsigned int sts = 0;
|
||
|
unsigned int wints = 0;
|
||
|
unsigned long tmo = 0;
|
||
|
unsigned long write_tmo = 0;
|
||
|
int ret = E_RES_PASS;
|
||
|
|
||
|
switch (opcode) {
|
||
|
case SD_IO_RW_DIRECT:
|
||
|
rawcmd = (1 << 7) | (52);
|
||
|
arg = (r_w_dirc << 31) | (func_num << 28)
|
||
|
| (base_addr << 9) | (*reg_value)
|
||
|
| ((r_w_dirc) ? 0x08000000 : 0x00000000);
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case SD_IO_RW_EXTENDED:
|
||
|
rawcmd = (4 << 16) | (r_w_dirc << 13)
|
||
|
| (1 << 11) | (1 << 7) | (53);
|
||
|
arg = (r_w_dirc << 31) | (func_num << 28)
|
||
|
| (base_addr << 9) | (0 << 26) | (0 << 27) | (4);
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
}
|
||
|
tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY), tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS MSDC busy tmo1\n");
|
||
|
ret = E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
/* clear fifo */
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
|
||
|
/* start command */
|
||
|
MSDC_WRITE32(SDC_ARG, arg);
|
||
|
MSDC_WRITE32(SDC_CMD, rawcmd);
|
||
|
|
||
|
/* wait interrupt status */
|
||
|
wints = MSDC_INT_CMDTMO | MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR;
|
||
|
tmo = AUTOK_CMD_TIMEOUT;
|
||
|
wait_cond_tmo(((sts = MSDC_READ32(MSDC_INT)) & wints), tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS wait int tmo\r\n");
|
||
|
ret |= E_RES_CMD_TMO;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
MSDC_WRITE32(MSDC_INT, (sts & wints));
|
||
|
if (sts == 0) {
|
||
|
ret |= E_RES_CMD_TMO;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (sts & MSDC_INT_CMDRDY)
|
||
|
ret |= E_RES_PASS;
|
||
|
else if (sts & MSDC_INT_RSPCRCERR) {
|
||
|
ret |= E_RES_RSP_CRC;
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS HW crc\r\n");
|
||
|
goto end;
|
||
|
} else if (sts & MSDC_INT_CMDTMO) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS HW tmo\r\n");
|
||
|
ret |= E_RES_CMD_TMO;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
tmo = jiffies + AUTOK_DAT_TIMEOUT;
|
||
|
while ((MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY) && (tmo != 0)) {
|
||
|
if (time_after(jiffies, tmo))
|
||
|
tmo = 0;
|
||
|
if ((r_w_dirc == EXT_WRITE) && (opcode == SD_IO_RW_EXTENDED)) {
|
||
|
MSDC_WRITE32(MSDC_TXDATA, *reg_value);
|
||
|
write_tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(
|
||
|
!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY),
|
||
|
write_tmo);
|
||
|
if (write_tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS MSDC busy tmo2\n");
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((tmo == 0) && (MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY)) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS MSDC busy tmo3...\n");
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
sts = MSDC_READ32(MSDC_INT);
|
||
|
wints = MSDC_INT_XFER_COMPL | MSDC_INT_DATCRCERR | MSDC_INT_DATTMO;
|
||
|
if (sts) {
|
||
|
/* clear status */
|
||
|
MSDC_WRITE32(MSDC_INT, (sts & wints));
|
||
|
if (sts & MSDC_INT_XFER_COMPL) {
|
||
|
if ((r_w_dirc == EXT_READ)
|
||
|
&& (opcode == SD_IO_RW_EXTENDED)) {
|
||
|
*reg_value = MSDC_READ32(MSDC_RXDATA);
|
||
|
}
|
||
|
ret |= E_RES_PASS;
|
||
|
}
|
||
|
if (MSDC_INT_DATCRCERR & sts) {
|
||
|
ret |= E_RES_DAT_CRC;
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS dat crc...\n");
|
||
|
}
|
||
|
if (MSDC_INT_DATTMO & sts) {
|
||
|
ret |= E_RES_DAT_TMO;
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS dat tmo...\n");
|
||
|
}
|
||
|
}
|
||
|
end:
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* define the function to shrink code's column */
|
||
|
static void rx_read(struct msdc_host *host, unsigned int value)
|
||
|
{
|
||
|
int i = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
|
||
|
for (i = 0; i < (MSDC_FIFO_SZ - 64)/4; i++)
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
}
|
||
|
|
||
|
static int autok_send_tune_cmd(struct msdc_host *host, unsigned int opcode,
|
||
|
enum TUNE_TYPE tune_type_value, struct autok_host *host_para)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int value;
|
||
|
unsigned int rawcmd = 0;
|
||
|
unsigned int arg = 0;
|
||
|
unsigned int sts = 0;
|
||
|
unsigned int wints = 0;
|
||
|
unsigned long tmo = 0;
|
||
|
unsigned long write_tmo = 0;
|
||
|
unsigned int left = 0;
|
||
|
unsigned int fifo_have = 0;
|
||
|
unsigned int fifo_1k_cnt = 0;
|
||
|
unsigned int i = 0;
|
||
|
int ret = E_RES_PASS;
|
||
|
unsigned int clk_tx_pre = 0;
|
||
|
|
||
|
switch (opcode) {
|
||
|
case MMC_SEND_EXT_CSD:
|
||
|
rawcmd = (512 << 16) | (0 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (8);
|
||
|
arg = 0;
|
||
|
if (tune_type_value == TUNE_LATCH_CK)
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, host->tune_latch_ck_cnt);
|
||
|
else
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_STOP_TRANSMISSION:
|
||
|
rawcmd = (1 << 14) | (7 << 7) | (12);
|
||
|
arg = 0;
|
||
|
break;
|
||
|
case MMC_SEND_STATUS:
|
||
|
rawcmd = (1 << 7) | (13);
|
||
|
arg = (1 << 16);
|
||
|
break;
|
||
|
case CHECK_QSR:
|
||
|
rawcmd = (1 << 7) | (13);
|
||
|
arg = (1 << 16) | (1 << 15);
|
||
|
break;
|
||
|
case MMC_SET_BLOCK_COUNT:
|
||
|
rawcmd = (1 << 7) | (23);
|
||
|
arg = 1;
|
||
|
break;
|
||
|
case MMC_WRITE_MULTIPLE_BLOCK:
|
||
|
rawcmd = (512 << 16) | (1 << 13)
|
||
|
| (2 << 11) | (1 << 7) | (25);
|
||
|
arg = TUNE_DATA_TX_ADDR;
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_SWITCH_CQ_EN:
|
||
|
rawcmd = (7 << 7) | (6);
|
||
|
arg = (3 << 24) | (15 << 16) | (1 << 8) | (1);
|
||
|
break;
|
||
|
case MMC_SWITCH_CQ_DIS:
|
||
|
rawcmd = (7 << 7) | (6);
|
||
|
arg = (3 << 24) | (15 << 16) | (0 << 8) | (1);
|
||
|
break;
|
||
|
case MMC_QUE_TASK_PARAMS_RD:
|
||
|
rawcmd = (1 << 7) | (44);
|
||
|
arg = (1 << 30) | (0 << 16) | (1);
|
||
|
break;
|
||
|
case MMC_QUE_TASK_PARAMS_WR:
|
||
|
rawcmd = (1 << 7) | (44);
|
||
|
arg = (0 << 30) | (0 << 16) | (1);
|
||
|
break;
|
||
|
case MMC_QUE_TASK_ADDR:
|
||
|
rawcmd = (1 << 7) | (45);
|
||
|
arg = TUNE_DATA_TX_ADDR;
|
||
|
break;
|
||
|
case MMC_EXECUTE_READ_TASK:
|
||
|
rawcmd = (512 << 16) | (0 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (46);
|
||
|
arg = (0 << 16);
|
||
|
if (tune_type_value == TUNE_LATCH_CK)
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, host->tune_latch_ck_cnt);
|
||
|
else
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_EXECUTE_WRITE_TASK:
|
||
|
rawcmd = (512 << 16) | (1 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (47);
|
||
|
arg = (0 << 16);
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_CMDQ_TASK_MGMT:
|
||
|
rawcmd = (1 << 14) | (7 << 7) | (48);
|
||
|
arg = (0 << 16) | (2);
|
||
|
break;
|
||
|
case MMC_READ_SINGLE_BLOCK:
|
||
|
left = 512;
|
||
|
rawcmd = (512 << 16) | (0 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (17);
|
||
|
arg = 0;
|
||
|
if (tune_type_value == TUNE_LATCH_CK)
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, host->tune_latch_ck_cnt);
|
||
|
else
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_SEND_TUNING_BLOCK:
|
||
|
left = 64;
|
||
|
rawcmd = (64 << 16) | (0 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (19);
|
||
|
arg = 0;
|
||
|
if (tune_type_value == TUNE_LATCH_CK)
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, host->tune_latch_ck_cnt);
|
||
|
else
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_SEND_TUNING_BLOCK_HS200:
|
||
|
left = 128;
|
||
|
rawcmd = (128 << 16) | (0 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (21);
|
||
|
arg = 0;
|
||
|
if (tune_type_value == TUNE_LATCH_CK)
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, host->tune_latch_ck_cnt);
|
||
|
else
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case MMC_WRITE_BLOCK:
|
||
|
/* get clk tx dly, for SD card tune clk tx */
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE0,
|
||
|
MSDC_PAD_TUNE0_CLKTXDLY,
|
||
|
clk_tx_pre);
|
||
|
rawcmd = (512 << 16) | (1 << 13)
|
||
|
| (1 << 11) | (1 << 7) | (24);
|
||
|
arg = TUNE_DATA_TX_ADDR;
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case SD_IO_RW_DIRECT:
|
||
|
rawcmd = (1 << 7) | (52);
|
||
|
arg = (0x80000000) | (1 << 28)
|
||
|
| (SDIO_CCCR_ABORT << 9) | (0);
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
case SD_IO_RW_EXTENDED:
|
||
|
rawcmd = (4 << 16) | (1 << 13) | (1 << 11) | (1 << 7) | (53);
|
||
|
arg = (0x80000000) | (1 << 28)
|
||
|
| (0xB0 << 9) | (0 << 26) | (0 << 27) | (4);
|
||
|
MSDC_WRITE32(SDC_BLK_NUM, 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY), tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]MSDC busy tmo1 cmd%d\n", opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
/* clear fifo */
|
||
|
if ((tune_type_value == TUNE_CMD) || (tune_type_value == TUNE_DATA)
|
||
|
|| (tune_type_value == TUNE_SDIO_PLUS)) {
|
||
|
if ((tune_type_value == TUNE_CMD)
|
||
|
&& (host->hw->host_function == MSDC_EMMC))
|
||
|
MSDC_WRITE32(MSDC_INT,
|
||
|
MSDC_INT_CMDTMO
|
||
|
| MSDC_INT_CMDRDY
|
||
|
| MSDC_INT_RSPCRCERR);
|
||
|
else {
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* start command */
|
||
|
MSDC_WRITE32(SDC_ARG, arg);
|
||
|
MSDC_WRITE32(SDC_CMD, rawcmd);
|
||
|
|
||
|
/* wait interrupt status */
|
||
|
wints = MSDC_INT_CMDTMO | MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR;
|
||
|
tmo = AUTOK_CMD_TIMEOUT;
|
||
|
wait_cond_tmo(((sts = MSDC_READ32(MSDC_INT)) & wints), tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD%d wait int tmo\r\n", opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
MSDC_WRITE32(MSDC_INT, (sts & wints));
|
||
|
if (sts == 0) {
|
||
|
ret |= E_RES_CMD_TMO;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (sts & MSDC_INT_CMDRDY) {
|
||
|
if (tune_type_value == TUNE_CMD) {
|
||
|
ret |= E_RES_PASS;
|
||
|
goto end;
|
||
|
}
|
||
|
} else if (sts & MSDC_INT_RSPCRCERR) {
|
||
|
ret |= E_RES_RSP_CRC;
|
||
|
if (tune_type_value != TUNE_SDIO_PLUS)
|
||
|
goto end;
|
||
|
} else if (sts & MSDC_INT_CMDTMO) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD%d HW tmo\r\n", opcode);
|
||
|
ret |= E_RES_CMD_TMO;
|
||
|
if (tune_type_value != TUNE_SDIO_PLUS)
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if ((tune_type_value != TUNE_LATCH_CK)
|
||
|
&& (tune_type_value != TUNE_DATA)
|
||
|
&& (tune_type_value != TUNE_SDIO_PLUS))
|
||
|
goto skip_tune_latch_ck_and_tune_data;
|
||
|
tmo = jiffies + AUTOK_DAT_TIMEOUT;
|
||
|
while ((MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY) && (tmo != 0)) {
|
||
|
if (time_after(jiffies, tmo))
|
||
|
tmo = 0;
|
||
|
if (tune_type_value == TUNE_LATCH_CK) {
|
||
|
fifo_have = msdc_rxfifocnt();
|
||
|
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200)
|
||
|
|| (opcode == MMC_READ_SINGLE_BLOCK)
|
||
|
|| (opcode == MMC_SEND_EXT_CSD)
|
||
|
|| (opcode == MMC_SEND_TUNING_BLOCK)
|
||
|
|| (opcode == MMC_EXECUTE_READ_TASK)) {
|
||
|
MSDC_SET_FIELD(MSDC_DBG_SEL,
|
||
|
0xffff << 0, 0x0b);
|
||
|
MSDC_GET_FIELD(MSDC_DBG_OUT,
|
||
|
0x7ff << 0, fifo_1k_cnt);
|
||
|
if ((fifo_1k_cnt >= MSDC_FIFO_THD_1K) &&
|
||
|
(fifo_have >= MSDC_FIFO_SZ) &&
|
||
|
(host_para->fifo_tune == 1)) {
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
} else if ((fifo_have >= MSDC_FIFO_SZ) &&
|
||
|
(host_para->fifo_tune == 0)) {
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
value = MSDC_READ32(MSDC_RXDATA);
|
||
|
}
|
||
|
}
|
||
|
} else if ((tune_type_value == TUNE_DATA)
|
||
|
&& ((opcode == MMC_WRITE_BLOCK)
|
||
|
|| (opcode == MMC_EXECUTE_WRITE_TASK)
|
||
|
|| (opcode == MMC_WRITE_MULTIPLE_BLOCK))) {
|
||
|
if (host->hw->host_function == MSDC_SD) {
|
||
|
MSDC_SET_FIELD(MSDC_PAD_TUNE0,
|
||
|
MSDC_PAD_TUNE0_CLKTXDLY,
|
||
|
host_para->clk_tx);
|
||
|
for (i = 0; i < 63; i++) {
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0xa5a5a5a5);
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0x1c345678);
|
||
|
}
|
||
|
/* restore clk tx brefore half data transfer */
|
||
|
MSDC_SET_FIELD(MSDC_PAD_TUNE0,
|
||
|
MSDC_PAD_TUNE0_CLKTXDLY,
|
||
|
clk_tx_pre);
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0xa5a5a5a5);
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0x1c345678);
|
||
|
} else {
|
||
|
for (i = 0; i < 64; i++) {
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0xf0f0f0f0);
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0x0f0f0f0f);
|
||
|
}
|
||
|
}
|
||
|
write_tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(
|
||
|
!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY),
|
||
|
write_tmo);
|
||
|
if (write_tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]MSDC busy tmo2 cmd%d\n",
|
||
|
opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
if (tune_type_value == TUNE_LATCH_CK) {
|
||
|
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200)
|
||
|
|| (opcode == MMC_READ_SINGLE_BLOCK)
|
||
|
|| (opcode == MMC_SEND_EXT_CSD)) {
|
||
|
fifo_have = msdc_rxfifocnt();
|
||
|
MSDC_SET_FIELD(MSDC_DBG_SEL,
|
||
|
0xffff << 0, 0x0b);
|
||
|
MSDC_GET_FIELD(MSDC_DBG_OUT,
|
||
|
0x7ff << 0, fifo_1k_cnt);
|
||
|
|
||
|
if ((fifo_1k_cnt >= MSDC_FIFO_THD_1K)
|
||
|
&& (fifo_have > MSDC_FIFO_SZ - 64))
|
||
|
rx_read(host, value);
|
||
|
}
|
||
|
}
|
||
|
} else if ((tune_type_value == TUNE_SDIO_PLUS)
|
||
|
&& (opcode == SD_IO_RW_EXTENDED)) {
|
||
|
MSDC_WRITE32(MSDC_TXDATA, 0x5a5a5a5a);
|
||
|
|
||
|
write_tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(
|
||
|
!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY),
|
||
|
write_tmo);
|
||
|
if (write_tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]MSDC busy tmo2 cmd%d\n",
|
||
|
opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((tmo == 0) && (MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY)) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]MSDC busy tmo3 cmd%d\n", opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
sts = MSDC_READ32(MSDC_INT);
|
||
|
wints = MSDC_INT_XFER_COMPL | MSDC_INT_DATCRCERR | MSDC_INT_DATTMO;
|
||
|
if (sts) {
|
||
|
/* clear status */
|
||
|
MSDC_WRITE32(MSDC_INT, (sts & wints));
|
||
|
if (sts & MSDC_INT_XFER_COMPL)
|
||
|
ret |= E_RES_PASS;
|
||
|
if (MSDC_INT_DATCRCERR & sts)
|
||
|
ret |= E_RES_DAT_CRC;
|
||
|
if (MSDC_INT_DATTMO & sts)
|
||
|
ret |= E_RES_DAT_TMO;
|
||
|
}
|
||
|
|
||
|
skip_tune_latch_ck_and_tune_data:
|
||
|
tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(!(MSDC_READ32(SDC_STS) & SDC_STS_SDCBUSY), tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]MSDC busy tmo4 cmd%d\n", opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
goto end;
|
||
|
}
|
||
|
if ((tune_type_value == TUNE_CMD) || (tune_type_value == TUNE_DATA))
|
||
|
msdc_clear_fifo();
|
||
|
|
||
|
end:
|
||
|
if ((opcode == MMC_STOP_TRANSMISSION)
|
||
|
|| (opcode == MMC_SWITCH_CQ_EN)
|
||
|
|| (opcode == MMC_SWITCH_CQ_DIS)
|
||
|
|| (opcode == MMC_CMDQ_TASK_MGMT)) {
|
||
|
tmo = AUTOK_DAT_TIMEOUT;
|
||
|
wait_cond_tmo(
|
||
|
((MSDC_READ32(MSDC_PS) & 0x10000) == 0x10000),
|
||
|
tmo);
|
||
|
if (tmo == 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DTA0 busy tmo cmd%d\n", opcode);
|
||
|
ret |= E_RES_FATAL_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int autok_simple_score(char *res_str, unsigned int result)
|
||
|
{
|
||
|
unsigned int bit = 0;
|
||
|
unsigned int num = 0;
|
||
|
unsigned int old = 0;
|
||
|
|
||
|
if (result == 0) {
|
||
|
/* maybe result is 0 */
|
||
|
strncpy(res_str, "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 33);
|
||
|
return 32;
|
||
|
}
|
||
|
if (result == 0xFFFFFFFF) {
|
||
|
strncpy(res_str, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* calc continue zero number */
|
||
|
while (bit < 32) {
|
||
|
if (result & (1 << bit)) {
|
||
|
res_str[bit] = 'X';
|
||
|
bit++;
|
||
|
if (old < num)
|
||
|
old = num;
|
||
|
num = 0;
|
||
|
continue;
|
||
|
}
|
||
|
res_str[bit] = 'O';
|
||
|
bit++;
|
||
|
num++;
|
||
|
}
|
||
|
if (num > old)
|
||
|
old = num;
|
||
|
|
||
|
res_str[32] = '\0';
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
static int autok_simple_score64(char *res_str64, u64 result64)
|
||
|
{
|
||
|
unsigned int bit = 0;
|
||
|
unsigned int num = 0;
|
||
|
unsigned int old = 0;
|
||
|
|
||
|
if (result64 == 0) {
|
||
|
/* maybe result is 0 */
|
||
|
strncpy(res_str64,
|
||
|
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||
|
65);
|
||
|
return 64;
|
||
|
}
|
||
|
if (result64 == 0xFFFFFFFFFFFFFFFF) {
|
||
|
strncpy(res_str64,
|
||
|
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||
|
65);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* calc continue zero number */
|
||
|
while (bit < 64) {
|
||
|
if (result64 & ((u64) (1LL << bit))) {
|
||
|
res_str64[bit] = 'X';
|
||
|
bit++;
|
||
|
if (old < num)
|
||
|
old = num;
|
||
|
num = 0;
|
||
|
continue;
|
||
|
}
|
||
|
res_str64[bit] = 'O';
|
||
|
bit++;
|
||
|
num++;
|
||
|
}
|
||
|
if (num > old)
|
||
|
old = num;
|
||
|
|
||
|
res_str64[64] = '\0';
|
||
|
return old;
|
||
|
}
|
||
|
|
||
|
enum {
|
||
|
RD_SCAN_NONE,
|
||
|
RD_SCAN_PAD_BOUND_S,
|
||
|
RD_SCAN_PAD_BOUND_E,
|
||
|
RD_SCAN_PAD_MARGIN,
|
||
|
};
|
||
|
|
||
|
static int autok_check_scan_res64(u64 rawdat,
|
||
|
struct AUTOK_SCAN_RES *scan_res, unsigned int bd_filter)
|
||
|
{
|
||
|
unsigned int bit;
|
||
|
struct BOUND_INFO *pBD = (struct BOUND_INFO *)scan_res->bd_info;
|
||
|
unsigned int RawScanSta = RD_SCAN_NONE;
|
||
|
|
||
|
for (bit = 0; bit < 64; bit++) {
|
||
|
if (rawdat & (1LL << bit)) {
|
||
|
switch (RawScanSta) {
|
||
|
case RD_SCAN_NONE:
|
||
|
RawScanSta = RD_SCAN_PAD_BOUND_S;
|
||
|
pBD->Bound_Start = 0;
|
||
|
pBD->Bound_width = 1;
|
||
|
scan_res->bd_cnt += 1;
|
||
|
break;
|
||
|
case RD_SCAN_PAD_MARGIN:
|
||
|
RawScanSta = RD_SCAN_PAD_BOUND_S;
|
||
|
pBD->Bound_Start = bit;
|
||
|
pBD->Bound_width = 1;
|
||
|
scan_res->bd_cnt += 1;
|
||
|
break;
|
||
|
case RD_SCAN_PAD_BOUND_E:
|
||
|
if ((bit - pBD->Bound_End) <= bd_filter) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_TRACE,
|
||
|
"[AUTOK]WARN: Try to filter the hole\r\n");
|
||
|
RawScanSta = RD_SCAN_PAD_BOUND_S;
|
||
|
|
||
|
pBD->Bound_width +=
|
||
|
(bit - pBD->Bound_End);
|
||
|
pBD->Bound_End = 0;
|
||
|
|
||
|
/* update full bound info */
|
||
|
if (pBD->is_fullbound) {
|
||
|
pBD->is_fullbound = 0;
|
||
|
scan_res->fbd_cnt -= 1;
|
||
|
}
|
||
|
} else {
|
||
|
/* No filter Check and
|
||
|
* Get the next boundary info
|
||
|
*/
|
||
|
RawScanSta = RD_SCAN_PAD_BOUND_S;
|
||
|
pBD++;
|
||
|
pBD->Bound_Start = bit;
|
||
|
pBD->Bound_width = 1;
|
||
|
scan_res->bd_cnt += 1;
|
||
|
if (scan_res->bd_cnt > BD_MAX_CNT)
|
||
|
goto end;
|
||
|
}
|
||
|
break;
|
||
|
case RD_SCAN_PAD_BOUND_S:
|
||
|
pBD->Bound_width++;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
switch (RawScanSta) {
|
||
|
case RD_SCAN_NONE:
|
||
|
RawScanSta = RD_SCAN_PAD_MARGIN;
|
||
|
break;
|
||
|
case RD_SCAN_PAD_BOUND_S:
|
||
|
RawScanSta = RD_SCAN_PAD_BOUND_E;
|
||
|
pBD->Bound_End = bit - 1;
|
||
|
/* update full bound info */
|
||
|
if (pBD->Bound_Start > 0) {
|
||
|
pBD->is_fullbound = 1;
|
||
|
scan_res->fbd_cnt += 1;
|
||
|
}
|
||
|
break;
|
||
|
case RD_SCAN_PAD_MARGIN:
|
||
|
case RD_SCAN_PAD_BOUND_E:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((pBD->Bound_End == 0) && (pBD->Bound_width != 0))
|
||
|
pBD->Bound_End = pBD->Bound_Start + pBD->Bound_width - 1;
|
||
|
|
||
|
end:
|
||
|
if (scan_res->bd_cnt > BD_MAX_CNT)
|
||
|
AUTOK_RAWPRINT("[AUTOK]WARN: %d bd Exist\r\n", BD_MAX_CNT);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_check_scan_res64_new(u64 rawdat,
|
||
|
struct AUTOK_SCAN_RES_NEW *scan_res, unsigned int bd_filter)
|
||
|
{
|
||
|
unsigned int bit;
|
||
|
int i, j;
|
||
|
/* bd fail and pass count */
|
||
|
unsigned char fail = 0;
|
||
|
unsigned char pass = 0;
|
||
|
enum AUTOK_TX_SCAN_STA_E RawScanSta = START_POSITION;
|
||
|
|
||
|
/* check scan window boundary */
|
||
|
for (bit = 0; bit < 64; bit++) {
|
||
|
if (rawdat & (1LL << bit)) {
|
||
|
switch (RawScanSta) {
|
||
|
case START_POSITION:
|
||
|
RawScanSta = FAIL_POSITION;
|
||
|
scan_res->fail_info[fail++].bd_s = bit;
|
||
|
scan_res->fail_cnt++;
|
||
|
break;
|
||
|
case PASS_POSITION:
|
||
|
RawScanSta = FAIL_POSITION;
|
||
|
if (bit == 63) {
|
||
|
scan_res->fail_info[fail++].bd_s = bit;
|
||
|
scan_res->fail_info[fail - 1].bd_e =
|
||
|
bit;
|
||
|
} else
|
||
|
scan_res->fail_info[fail++].bd_s = bit;
|
||
|
scan_res->pass_info[pass - 1].bd_e = bit - 1;
|
||
|
scan_res->fail_cnt++;
|
||
|
break;
|
||
|
case FAIL_POSITION:
|
||
|
RawScanSta = FAIL_POSITION;
|
||
|
if (bit == 63)
|
||
|
scan_res->fail_info[fail - 1].bd_e =
|
||
|
bit;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
switch (RawScanSta) {
|
||
|
case START_POSITION:
|
||
|
RawScanSta = PASS_POSITION;
|
||
|
scan_res->pass_info[pass++].bd_s = bit;
|
||
|
scan_res->pass_cnt++;
|
||
|
break;
|
||
|
case PASS_POSITION:
|
||
|
RawScanSta = PASS_POSITION;
|
||
|
if (bit == 63)
|
||
|
scan_res->pass_info[pass - 1].bd_e =
|
||
|
bit;
|
||
|
break;
|
||
|
case FAIL_POSITION:
|
||
|
RawScanSta = PASS_POSITION;
|
||
|
if (bit == 63) {
|
||
|
scan_res->pass_info[pass++].bd_s = bit;
|
||
|
scan_res->pass_info[pass - 1].bd_e =
|
||
|
bit;
|
||
|
} else
|
||
|
scan_res->pass_info[pass++].bd_s = bit;
|
||
|
scan_res->fail_info[fail - 1].bd_e = bit - 1;
|
||
|
scan_res->pass_cnt++;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = scan_res->fail_cnt; i >= 0; i--) {
|
||
|
if (i > scan_res->fail_cnt)
|
||
|
break;
|
||
|
if ((i >= 1) && ((scan_res->fail_info[i].bd_s
|
||
|
- scan_res->fail_info[i - 1].bd_e - 1) < bd_filter)) {
|
||
|
scan_res->fail_info[i - 1].bd_e =
|
||
|
scan_res->fail_info[i].bd_e;
|
||
|
scan_res->fail_info[i].bd_s = 0;
|
||
|
scan_res->fail_info[i].bd_e = 0;
|
||
|
for (j = i; j < (scan_res->fail_cnt - 1); j++) {
|
||
|
scan_res->fail_info[j].bd_s =
|
||
|
scan_res->fail_info[j + 1].bd_s;
|
||
|
scan_res->fail_info[j].bd_e =
|
||
|
scan_res->fail_info[j + 1].bd_e;
|
||
|
}
|
||
|
/* add check to prevent coverity scan fail */
|
||
|
if (scan_res->fail_cnt >= 1) {
|
||
|
scan_res->fail_info[
|
||
|
scan_res->fail_cnt - 1].bd_s = 0;
|
||
|
scan_res->fail_info[
|
||
|
scan_res->fail_cnt - 1].bd_e = 0;
|
||
|
} else
|
||
|
WARN_ON(1);
|
||
|
scan_res->fail_cnt--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_pad_dly_corner_check(struct AUTOK_REF_INFO *pInfo)
|
||
|
{
|
||
|
/* scan result @ rising edge */
|
||
|
struct AUTOK_SCAN_RES *pBdInfo_R = NULL;
|
||
|
/* scan result @ falling edge */
|
||
|
struct AUTOK_SCAN_RES *pBdInfo_F = NULL;
|
||
|
struct AUTOK_SCAN_RES *p_Temp[2] = {NULL};
|
||
|
unsigned int i, j, k, l;
|
||
|
unsigned int pass_bd_size[BD_MAX_CNT + 1];
|
||
|
unsigned int max_pass = 0;
|
||
|
unsigned int max_size = 0;
|
||
|
unsigned int bd_max_size = 0;
|
||
|
unsigned int bd_overlap = 0;
|
||
|
unsigned int corner_case_flag = 0;
|
||
|
|
||
|
pBdInfo_R = &(pInfo->scan_info[0]);
|
||
|
pBdInfo_F = &(pInfo->scan_info[1]);
|
||
|
/*
|
||
|
* for corner case
|
||
|
* oooooooooooooooooo rising has no fail bound
|
||
|
* oooooooooooooooooo falling has no fail bound
|
||
|
*/
|
||
|
if ((pBdInfo_R->bd_cnt == 0) && (pBdInfo_F->bd_cnt == 0)) {
|
||
|
AUTOK_RAWPRINT("[ATUOK]Warn:can't find bd both edge\r\n");
|
||
|
pInfo->opt_dly_cnt = 31;
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
return -1;
|
||
|
}
|
||
|
/*
|
||
|
* for corner case
|
||
|
* xxxxxxxxxxxxxxxxxxxx rising only has one boundary,but all fail
|
||
|
* oooooooooxxooooooo falling has normal boundary
|
||
|
* or
|
||
|
* ooooooooooooxooooo rising has normal boundary
|
||
|
* xxxxxxxxxxxxxxxxxxxx falling only has one boundary,but all fail
|
||
|
*/
|
||
|
if ((pBdInfo_R->bd_cnt == 1) && (pBdInfo_F->bd_cnt == 1)
|
||
|
&& (pBdInfo_R->bd_info[0].Bound_Start == 0)
|
||
|
&& (pBdInfo_R->bd_info[0].Bound_End == 63)
|
||
|
&& (pBdInfo_F->bd_info[0].Bound_Start == 0)
|
||
|
&& (pBdInfo_F->bd_info[0].Bound_End == 63)) {
|
||
|
AUTOK_RAWPRINT("[ATUOK]Err:can't find window both edge\r\n");
|
||
|
return -2;
|
||
|
}
|
||
|
/*
|
||
|
* for shamoo case
|
||
|
* xxxooooooxxxxooooxxx rising has more than 3 boundary
|
||
|
* xxxooooooxxxxooooxxx failing has more than 3 boundary
|
||
|
*/
|
||
|
if ((pBdInfo_R->bd_cnt >= 3) && (pBdInfo_F->bd_cnt >= 3)) {
|
||
|
AUTOK_RAWPRINT("[ATUOK]Err:data window shamoo\r\n");
|
||
|
return -2;
|
||
|
}
|
||
|
/*
|
||
|
* for corner case
|
||
|
* xxxxxxxxxxxxxxxxxxxx rising only has one boundary,but all fail
|
||
|
* oooooooooxxooooooo falling has normal boundary
|
||
|
* or
|
||
|
* ooooooooooooxooooo rising has normal boundary
|
||
|
* xxxxxxxxxxxxxxxxxxxx falling only has one boundary,but all fail
|
||
|
*/
|
||
|
for (j = 0; j < 2; j++) {
|
||
|
if (j == 0) {
|
||
|
p_Temp[0] = pBdInfo_R;
|
||
|
p_Temp[1] = pBdInfo_F;
|
||
|
} else {
|
||
|
p_Temp[0] = pBdInfo_F;
|
||
|
p_Temp[1] = pBdInfo_R;
|
||
|
}
|
||
|
/* check boundary overlap */
|
||
|
for (k = 0; k < p_Temp[0]->bd_cnt; k++)
|
||
|
for (l = 0; l < p_Temp[1]->bd_cnt; l++)
|
||
|
if (((p_Temp[0]->bd_info[k].Bound_Start
|
||
|
>= p_Temp[1]->bd_info[l].Bound_Start)
|
||
|
&& (p_Temp[0]->bd_info[k].Bound_Start
|
||
|
<= p_Temp[1]->bd_info[l].Bound_End))
|
||
|
|| ((p_Temp[0]->bd_info[k].Bound_End
|
||
|
<= p_Temp[1]->bd_info[l].Bound_End)
|
||
|
&& (p_Temp[0]->bd_info[k].Bound_End
|
||
|
>= p_Temp[1]->bd_info[l].Bound_Start))
|
||
|
|| ((p_Temp[1]->bd_info[l].Bound_Start
|
||
|
>= p_Temp[0]->bd_info[k].Bound_Start)
|
||
|
&& (p_Temp[1]->bd_info[l].Bound_Start
|
||
|
<= p_Temp[0]->bd_info[k].Bound_End)))
|
||
|
bd_overlap = 1;
|
||
|
/*check max boundary size */
|
||
|
for (k = 0; k < p_Temp[0]->bd_cnt; k++) {
|
||
|
if ((p_Temp[0]->bd_info[k].Bound_End
|
||
|
- p_Temp[0]->bd_info[k].Bound_Start)
|
||
|
>= 20)
|
||
|
bd_max_size = 1;
|
||
|
}
|
||
|
if (((bd_overlap == 1)
|
||
|
&& (bd_max_size == 1))
|
||
|
|| ((p_Temp[1]->bd_cnt == 0)
|
||
|
&& (bd_max_size == 1))) {
|
||
|
corner_case_flag = 1;
|
||
|
}
|
||
|
if (((p_Temp[0]->bd_cnt == 1)
|
||
|
&& (p_Temp[0]->bd_info[0].Bound_Start == 0)
|
||
|
&& (p_Temp[0]->bd_info[0].Bound_End == 63))
|
||
|
|| (corner_case_flag == 1)) {
|
||
|
if (j == 0)
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
else
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
/* 1T calc fail,need check max pass bd,select mid */
|
||
|
switch (p_Temp[1]->bd_cnt) {
|
||
|
case 4:
|
||
|
pass_bd_size[0] =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start - 0;
|
||
|
pass_bd_size[1] =
|
||
|
p_Temp[1]->bd_info[1].Bound_Start
|
||
|
- p_Temp[1]->bd_info[0].Bound_End;
|
||
|
pass_bd_size[2] =
|
||
|
p_Temp[1]->bd_info[2].Bound_Start
|
||
|
- p_Temp[1]->bd_info[1].Bound_End;
|
||
|
pass_bd_size[3] =
|
||
|
p_Temp[1]->bd_info[3].Bound_Start
|
||
|
- p_Temp[1]->bd_info[2].Bound_End;
|
||
|
pass_bd_size[4] =
|
||
|
63 - p_Temp[1]->bd_info[3].Bound_End;
|
||
|
max_size = pass_bd_size[0];
|
||
|
max_pass = 0;
|
||
|
for (i = 0; i < 5; i++) {
|
||
|
if (pass_bd_size[i] >= max_size) {
|
||
|
max_size = pass_bd_size[i];
|
||
|
max_pass = i;
|
||
|
}
|
||
|
}
|
||
|
if (max_pass == 0)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start
|
||
|
/ 2;
|
||
|
else if (max_pass == 4)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(63 +
|
||
|
p_Temp[1]->bd_info[3].Bound_End)
|
||
|
/ 2;
|
||
|
else {
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(p_Temp[1]->bd_info[max_pass].Bound_Start
|
||
|
+
|
||
|
p_Temp[1]->bd_info[max_pass - 1].Bound_End)
|
||
|
/ 2;
|
||
|
}
|
||
|
break;
|
||
|
case 3:
|
||
|
pass_bd_size[0] =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start - 0;
|
||
|
pass_bd_size[1] =
|
||
|
p_Temp[1]->bd_info[1].Bound_Start
|
||
|
- p_Temp[1]->bd_info[0].Bound_End;
|
||
|
pass_bd_size[2] =
|
||
|
p_Temp[1]->bd_info[2].Bound_Start
|
||
|
- p_Temp[1]->bd_info[1].Bound_End;
|
||
|
pass_bd_size[3] =
|
||
|
63 - p_Temp[1]->bd_info[2].Bound_End;
|
||
|
max_size = pass_bd_size[0];
|
||
|
max_pass = 0;
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
if (pass_bd_size[i] >= max_size) {
|
||
|
max_size = pass_bd_size[i];
|
||
|
max_pass = i;
|
||
|
}
|
||
|
}
|
||
|
if (max_pass == 0)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start / 2;
|
||
|
else if (max_pass == 3)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(63 + p_Temp[1]->bd_info[2].Bound_End) / 2;
|
||
|
else {
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(p_Temp[1]->bd_info[max_pass].Bound_Start
|
||
|
+
|
||
|
p_Temp[1]->bd_info[max_pass - 1].Bound_End)
|
||
|
/ 2;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
pass_bd_size[0] =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start - 0;
|
||
|
pass_bd_size[1] =
|
||
|
p_Temp[1]->bd_info[1].Bound_Start
|
||
|
- p_Temp[1]->bd_info[0].Bound_End;
|
||
|
pass_bd_size[2] =
|
||
|
63 - p_Temp[1]->bd_info[1].Bound_End;
|
||
|
max_size = pass_bd_size[0];
|
||
|
max_pass = 0;
|
||
|
for (i = 0; i < 3; i++) {
|
||
|
if (pass_bd_size[i] >= max_size) {
|
||
|
max_size = pass_bd_size[i];
|
||
|
max_pass = i;
|
||
|
}
|
||
|
}
|
||
|
if (max_pass == 0)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start / 2;
|
||
|
else if (max_pass == 2)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(63 + p_Temp[1]->bd_info[1].Bound_End) / 2;
|
||
|
else {
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(p_Temp[1]->bd_info[max_pass].Bound_Start
|
||
|
+
|
||
|
p_Temp[1]->bd_info[max_pass - 1].Bound_End)
|
||
|
/ 2;
|
||
|
}
|
||
|
break;
|
||
|
case 1:
|
||
|
pass_bd_size[0] =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start - 0;
|
||
|
pass_bd_size[1] =
|
||
|
63 -
|
||
|
p_Temp[1]->bd_info[0].Bound_End;
|
||
|
max_size = pass_bd_size[0];
|
||
|
max_pass = 0;
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
if (pass_bd_size[i] >= max_size) {
|
||
|
max_size = pass_bd_size[i];
|
||
|
max_pass = i;
|
||
|
}
|
||
|
}
|
||
|
if (max_pass == 0)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
p_Temp[1]->bd_info[0].Bound_Start
|
||
|
/ 2;
|
||
|
else if (max_pass == 1)
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(63 +
|
||
|
p_Temp[1]->bd_info[0].Bound_End)
|
||
|
/ 2;
|
||
|
break;
|
||
|
case 0:
|
||
|
pInfo->opt_dly_cnt = 31;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_pad_dly_sel(struct AUTOK_REF_INFO *pInfo)
|
||
|
{
|
||
|
/* scan result @ rising edge */
|
||
|
struct AUTOK_SCAN_RES *pBdInfo_R = NULL;
|
||
|
/* scan result @ falling edge */
|
||
|
struct AUTOK_SCAN_RES *pBdInfo_F = NULL;
|
||
|
/* Save the first boundary info for calc optimised dly count */
|
||
|
struct BOUND_INFO *pBdPrev = NULL;
|
||
|
/* Save the second boundary info for calc optimised dly count */
|
||
|
struct BOUND_INFO *pBdNext = NULL;
|
||
|
struct BOUND_INFO *pBdTmp = NULL;
|
||
|
/* Full Boundary count */
|
||
|
unsigned int FBound_Cnt_R = 0;
|
||
|
unsigned int Bound_Cnt_R = 0;
|
||
|
unsigned int Bound_Cnt_F = 0;
|
||
|
unsigned int cycle_cnt = 64;
|
||
|
int uBD_mid_prev = 0;
|
||
|
int uBD_mid_next = 0;
|
||
|
int uBD_width = 3;
|
||
|
int uDlySel_F = 0;
|
||
|
int uDlySel_R = 0;
|
||
|
/* for falling edge margin compress */
|
||
|
int uMgLost_F = 0;
|
||
|
/* for rising edge margin compress */
|
||
|
int uMgLost_R = 0;
|
||
|
unsigned int ret = 0;
|
||
|
unsigned int i;
|
||
|
int corner_res = 0;
|
||
|
|
||
|
pBdInfo_R = &(pInfo->scan_info[0]);
|
||
|
pBdInfo_F = &(pInfo->scan_info[1]);
|
||
|
FBound_Cnt_R = pBdInfo_R->fbd_cnt;
|
||
|
Bound_Cnt_R = pBdInfo_R->bd_cnt;
|
||
|
Bound_Cnt_F = pBdInfo_F->bd_cnt;
|
||
|
|
||
|
corner_res = autok_pad_dly_corner_check(pInfo);
|
||
|
if (corner_res == -1)
|
||
|
return 0;
|
||
|
else if (corner_res == -2)
|
||
|
return -2;
|
||
|
|
||
|
switch (FBound_Cnt_R) {
|
||
|
case 4: /* SSSS Corner may cover 2~3T */
|
||
|
case 3:
|
||
|
AUTOK_RAWPRINT("[ATUOK]Warn:Many Full bd cnt:%d\r\n",
|
||
|
FBound_Cnt_R);
|
||
|
case 2: /* mode_1 : 2 full boudary */
|
||
|
for (i = 0; i < BD_MAX_CNT; i++) {
|
||
|
if (pBdInfo_R->bd_info[i].is_fullbound) {
|
||
|
if (pBdPrev == NULL) {
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[i]);
|
||
|
} else {
|
||
|
pBdNext = &(pBdInfo_R->bd_info[i]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pBdPrev && pBdNext) {
|
||
|
uBD_mid_prev =
|
||
|
(pBdPrev->Bound_Start + pBdPrev->Bound_End) / 2;
|
||
|
uBD_mid_next =
|
||
|
(pBdNext->Bound_Start + pBdNext->Bound_End) / 2;
|
||
|
/* while in 2 full bound case, bd_width calc */
|
||
|
uBD_width =
|
||
|
(pBdPrev->Bound_width +
|
||
|
pBdNext->Bound_width) / 2;
|
||
|
cycle_cnt = uBD_mid_next - uBD_mid_prev;
|
||
|
/* delay count sel at rising edge */
|
||
|
if (uBD_mid_prev >= cycle_cnt / 2) {
|
||
|
uDlySel_R = uBD_mid_prev - cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
} else if ((cycle_cnt / 2 -
|
||
|
uBD_mid_prev) >
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
uDlySel_R = uBD_mid_prev + cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
} else {
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R = cycle_cnt / 2 - uBD_mid_prev;
|
||
|
}
|
||
|
/* delay count sel at falling edge */
|
||
|
pBdTmp = &(pBdInfo_R->bd_info[0]);
|
||
|
if (pBdTmp->is_fullbound) {
|
||
|
/* ooooxxxooooooxxxooo */
|
||
|
uDlySel_F = uBD_mid_prev;
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
/* xooooooxxxoooooooxxxoo */
|
||
|
if (pBdTmp->Bound_End > uBD_width / 2) {
|
||
|
uDlySel_F =
|
||
|
(pBdTmp->Bound_End) -
|
||
|
(uBD_width / 2);
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
uDlySel_F = 0;
|
||
|
uMgLost_F =
|
||
|
(uBD_width / 2) -
|
||
|
(pBdTmp->Bound_End);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* error can not find 2 foull boary */
|
||
|
AUTOK_RAWPRINT("[AUTOK]can not find 2 full bd @Mode1");
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 1: /* rising edge find one full boundary */
|
||
|
if (Bound_Cnt_R > 1) {
|
||
|
/* mode_2: 1 full boundary and boundary count > 1 */
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
pBdNext = &(pBdInfo_R->bd_info[1]);
|
||
|
|
||
|
if (pBdPrev->is_fullbound)
|
||
|
uBD_width = pBdPrev->Bound_width;
|
||
|
else
|
||
|
uBD_width = pBdNext->Bound_width;
|
||
|
|
||
|
if ((pBdPrev->is_fullbound)
|
||
|
|| (pBdNext->is_fullbound)) {
|
||
|
if (pBdPrev->Bound_Start > 0)
|
||
|
cycle_cnt =
|
||
|
pBdNext->Bound_Start -
|
||
|
pBdPrev->Bound_Start;
|
||
|
else
|
||
|
cycle_cnt =
|
||
|
pBdNext->Bound_End -
|
||
|
pBdPrev->Bound_End;
|
||
|
|
||
|
/* delay count sel@rising & falling edge */
|
||
|
if (pBdPrev->is_fullbound) {
|
||
|
uBD_mid_prev =
|
||
|
(pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
uDlySel_F = uBD_mid_prev;
|
||
|
uMgLost_F = 0;
|
||
|
if (uBD_mid_prev >= cycle_cnt / 2) {
|
||
|
uDlySel_R =
|
||
|
uBD_mid_prev -
|
||
|
cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
} else if ((cycle_cnt / 2 -
|
||
|
uBD_mid_prev) >
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
uDlySel_R =
|
||
|
uBD_mid_prev +
|
||
|
cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
} else {
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R =
|
||
|
cycle_cnt / 2 -
|
||
|
uBD_mid_prev;
|
||
|
}
|
||
|
} else {
|
||
|
/* first boundary not full boudary */
|
||
|
uBD_mid_next =
|
||
|
(pBdNext->Bound_Start +
|
||
|
pBdNext->Bound_End) / 2;
|
||
|
uDlySel_R =
|
||
|
uBD_mid_next - cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
if (pBdPrev->Bound_End >
|
||
|
uBD_width / 2) {
|
||
|
uDlySel_F =
|
||
|
(pBdPrev->Bound_End) -
|
||
|
(uBD_width / 2);
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
uDlySel_F = 0;
|
||
|
uMgLost_F =
|
||
|
(uBD_width / 2) -
|
||
|
(pBdPrev->Bound_End);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* full bound must in first 2 boundary */
|
||
|
return -1;
|
||
|
}
|
||
|
} else if (Bound_Cnt_F > 0) {
|
||
|
/* mode_3: 1 full boundary and
|
||
|
* only one boundary exist @rising edge
|
||
|
* this boundary is full bound
|
||
|
*/
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
pBdNext = &(pBdInfo_F->bd_info[0]);
|
||
|
uBD_mid_prev =
|
||
|
(pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
uBD_width = pBdPrev->Bound_width;
|
||
|
|
||
|
if (pBdNext->Bound_Start == 0) {
|
||
|
cycle_cnt =
|
||
|
(pBdPrev->Bound_End -
|
||
|
pBdNext->Bound_End) * 2;
|
||
|
} else if (pBdNext->Bound_End == 63) {
|
||
|
cycle_cnt =
|
||
|
(pBdNext->Bound_Start -
|
||
|
pBdPrev->Bound_Start) * 2;
|
||
|
} else {
|
||
|
uBD_mid_next =
|
||
|
(pBdNext->Bound_Start +
|
||
|
pBdNext->Bound_End) / 2;
|
||
|
|
||
|
if (uBD_mid_next > uBD_mid_prev)
|
||
|
cycle_cnt =
|
||
|
(uBD_mid_next -
|
||
|
uBD_mid_prev) * 2;
|
||
|
else
|
||
|
cycle_cnt =
|
||
|
(uBD_mid_prev -
|
||
|
uBD_mid_next) * 2;
|
||
|
}
|
||
|
|
||
|
uDlySel_F = uBD_mid_prev;
|
||
|
uMgLost_F = 0;
|
||
|
|
||
|
if (uBD_mid_prev >= cycle_cnt / 2) {
|
||
|
/* case 1 */
|
||
|
uDlySel_R = uBD_mid_prev - cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
} else if (cycle_cnt / 2 -
|
||
|
uBD_mid_prev <=
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
/* case 2 */
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R =
|
||
|
cycle_cnt / 2 -
|
||
|
uBD_mid_prev;
|
||
|
} else if (cycle_cnt / 2
|
||
|
+ uBD_mid_prev <= 63) {
|
||
|
/* case 3 */
|
||
|
uDlySel_R = cycle_cnt / 2 + uBD_mid_prev;
|
||
|
uMgLost_R = 0;
|
||
|
} else if (32 -
|
||
|
uBD_mid_prev <=
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
/* case 4 */
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R =
|
||
|
cycle_cnt / 2 -
|
||
|
uBD_mid_prev;
|
||
|
} else {
|
||
|
/* case 5 */
|
||
|
uDlySel_R = 63;
|
||
|
uMgLost_R =
|
||
|
uBD_mid_prev +
|
||
|
cycle_cnt / 2 - 63;
|
||
|
}
|
||
|
} else {
|
||
|
/* mode_4: falling edge no boundary found
|
||
|
* & rising edge only one full boundary exist
|
||
|
* this boundary is full bound
|
||
|
*/
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
uBD_mid_prev =
|
||
|
(pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
uBD_width = pBdPrev->Bound_width;
|
||
|
|
||
|
if (pBdPrev->Bound_End >
|
||
|
(64 - pBdPrev->Bound_Start))
|
||
|
cycle_cnt = 2 *
|
||
|
(pBdPrev->Bound_End + 1);
|
||
|
else
|
||
|
cycle_cnt = 2 *
|
||
|
(64 - pBdPrev->Bound_Start);
|
||
|
|
||
|
uDlySel_R = 0xFF;
|
||
|
/* Margin enough donot care margin lost */
|
||
|
uMgLost_R = 0xFF;
|
||
|
uDlySel_F = uBD_mid_prev;
|
||
|
/* Margin enough donot care margin lost */
|
||
|
uMgLost_F = 0xFF;
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK]Warn: 1T > %d\r\n",
|
||
|
cycle_cnt);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0: /* rising edge cannot find full boudary */
|
||
|
if (Bound_Cnt_R == 2) {
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
/* this boundary is full bound */
|
||
|
pBdNext = &(pBdInfo_F->bd_info[0]);
|
||
|
|
||
|
if (pBdNext->is_fullbound) {
|
||
|
/* mode_5: rising_edge 2 boundary
|
||
|
* (not full bound),
|
||
|
* falling edge 1 full boundary
|
||
|
*/
|
||
|
uBD_width = pBdNext->Bound_width;
|
||
|
cycle_cnt = 2 *
|
||
|
(pBdNext->Bound_End -
|
||
|
pBdPrev->Bound_End);
|
||
|
uBD_mid_next =
|
||
|
(pBdNext->Bound_Start +
|
||
|
pBdNext->Bound_End) / 2;
|
||
|
uDlySel_R = uBD_mid_next;
|
||
|
uMgLost_R = 0;
|
||
|
if (pBdPrev->Bound_End >= uBD_width / 2) {
|
||
|
uDlySel_F =
|
||
|
pBdPrev->Bound_End - uBD_width / 2;
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
uDlySel_F = 0;
|
||
|
uMgLost_F =
|
||
|
uBD_width / 2 -
|
||
|
pBdPrev->Bound_End;
|
||
|
}
|
||
|
} else {
|
||
|
/* for falling edge there must be
|
||
|
* one full boundary between two
|
||
|
* bounary_mid at rising
|
||
|
* this is a corner case,
|
||
|
* falling boundary may scan miss.
|
||
|
* xoooooooooooooooox
|
||
|
* oooooooooooooooooo
|
||
|
* or
|
||
|
* xoooooooooooooooox
|
||
|
* xxoooooooooooooooo
|
||
|
* or
|
||
|
* xoooooooooooooooox
|
||
|
* ooooooooooooooooox
|
||
|
*/
|
||
|
pInfo->cycle_cnt =
|
||
|
pBdInfo_R->bd_info[1].Bound_End
|
||
|
- pBdInfo_R->bd_info[0].Bound_Start;
|
||
|
if (Bound_Cnt_F == 0) {
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
pInfo->opt_dly_cnt = 0;
|
||
|
} else {
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(pBdInfo_R->bd_info[1].Bound_End
|
||
|
+ pBdInfo_R->bd_info[0].Bound_Start)
|
||
|
/ 2;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
} else if (Bound_Cnt_R == 1) {
|
||
|
if (Bound_Cnt_F > 1) {
|
||
|
/* when riss edge have only one bd (not full bd)
|
||
|
* falling edge shouldn't more than 1Bound exist
|
||
|
* this is a corner case, rise bd may scan miss
|
||
|
* xooooooooooooooooo
|
||
|
* oooxooooooooxooooo
|
||
|
*/
|
||
|
pInfo->cycle_cnt =
|
||
|
(pBdInfo_F->bd_info[1].Bound_End
|
||
|
+ pBdInfo_F->bd_info[1].Bound_Start) / 2
|
||
|
- (pBdInfo_F->bd_info[0].Bound_End
|
||
|
+ pBdInfo_F->bd_info[0].Bound_Start) / 2;
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
pInfo->opt_dly_cnt =
|
||
|
((pBdInfo_F->bd_info[1].Bound_End
|
||
|
+ pBdInfo_F->bd_info[1].Bound_Start) / 2
|
||
|
+ (pBdInfo_F->bd_info[0].Bound_End
|
||
|
+ pBdInfo_F->bd_info[0].Bound_Start) / 2) / 2;
|
||
|
return ret;
|
||
|
} else if (Bound_Cnt_F == 1) {
|
||
|
/* mode_6: rise edge only 1 bd (not full bd)
|
||
|
* & fall edge have only 1 bound too
|
||
|
*/
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
pBdNext = &(pBdInfo_F->bd_info[0]);
|
||
|
if (pBdNext->is_fullbound) {
|
||
|
uBD_width =
|
||
|
pBdNext->Bound_width;
|
||
|
} else {
|
||
|
if (pBdNext->Bound_width >
|
||
|
pBdPrev->Bound_width)
|
||
|
uBD_width =
|
||
|
(pBdNext->Bound_width + 1);
|
||
|
else
|
||
|
uBD_width =
|
||
|
(pBdPrev->Bound_width + 1);
|
||
|
|
||
|
if (uBD_width < AUTOK_BD_WIDTH_REF)
|
||
|
uBD_width = AUTOK_BD_WIDTH_REF;
|
||
|
} /* Boundary width calc done */
|
||
|
|
||
|
if (pBdPrev->Bound_Start == 0) {
|
||
|
/* Current Desing Not Allowed */
|
||
|
if (pBdNext->Bound_Start == 0) {
|
||
|
/* Current Desing Not Allowed
|
||
|
* this is a corner case,
|
||
|
* boundary may scan error.
|
||
|
* xooooooooooooooooo
|
||
|
* xooooooooooooooooo
|
||
|
*/
|
||
|
pInfo->cycle_cnt = 2 *
|
||
|
(64 -
|
||
|
(pBdInfo_R->bd_info[0].Bound_End +
|
||
|
pBdInfo_R->bd_info[0].Bound_Start)
|
||
|
/ 2);
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt = 31;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
cycle_cnt =
|
||
|
(pBdNext->Bound_Start -
|
||
|
pBdPrev->Bound_End +
|
||
|
uBD_width) * 2;
|
||
|
} else if (pBdPrev->Bound_End == 63) {
|
||
|
/* Current Desing Not Allowed */
|
||
|
if (pBdNext->Bound_End == 63) {
|
||
|
/* Current Desing Not Allowed
|
||
|
* this is a corner case,
|
||
|
* boundary may scan error.
|
||
|
* ooooooooooooooooox
|
||
|
* ooooooooooooooooox
|
||
|
*/
|
||
|
pInfo->cycle_cnt =
|
||
|
pBdInfo_R->bd_info[0].Bound_End +
|
||
|
pBdInfo_R->bd_info[0].Bound_Start;
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt = 31;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
cycle_cnt =
|
||
|
(pBdPrev->Bound_Start -
|
||
|
pBdNext->Bound_End +
|
||
|
uBD_width) * 2;
|
||
|
} /* cycle count calc done */
|
||
|
|
||
|
/* calc optimise delay count */
|
||
|
if (pBdPrev->Bound_Start == 0) {
|
||
|
/* falling edge sel */
|
||
|
if (pBdPrev->Bound_End >=
|
||
|
uBD_width / 2) {
|
||
|
uDlySel_F =
|
||
|
pBdPrev->Bound_End -
|
||
|
uBD_width / 2;
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
uDlySel_F = 0;
|
||
|
uMgLost_F =
|
||
|
uBD_width / 2 -
|
||
|
pBdPrev->Bound_End;
|
||
|
}
|
||
|
|
||
|
/* rising edge sel */
|
||
|
if (pBdPrev->Bound_End -
|
||
|
uBD_width / 2 +
|
||
|
cycle_cnt / 2 > 63) {
|
||
|
uDlySel_R = 63;
|
||
|
uMgLost_R =
|
||
|
pBdPrev->Bound_End -
|
||
|
uBD_width / 2 +
|
||
|
cycle_cnt / 2 - 63;
|
||
|
} else {
|
||
|
uDlySel_R =
|
||
|
pBdPrev->Bound_End -
|
||
|
uBD_width / 2 +
|
||
|
cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
}
|
||
|
} else if (pBdPrev->Bound_End == 63) {
|
||
|
/* falling edge sel */
|
||
|
if (pBdPrev->Bound_Start +
|
||
|
uBD_width / 2 < 63) {
|
||
|
uDlySel_F =
|
||
|
pBdPrev->Bound_Start +
|
||
|
uBD_width / 2;
|
||
|
uMgLost_F = 0;
|
||
|
} else {
|
||
|
uDlySel_F = 63;
|
||
|
uMgLost_F =
|
||
|
pBdPrev->Bound_Start +
|
||
|
uBD_width / 2 - 63;
|
||
|
}
|
||
|
|
||
|
/* rising edge sel */
|
||
|
if (pBdPrev->Bound_Start +
|
||
|
uBD_width / 2 -
|
||
|
cycle_cnt / 2 < 0) {
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R =
|
||
|
cycle_cnt / 2 -
|
||
|
(pBdPrev->Bound_Start +
|
||
|
uBD_width / 2);
|
||
|
} else {
|
||
|
uDlySel_R =
|
||
|
pBdPrev->Bound_Start +
|
||
|
uBD_width / 2 -
|
||
|
cycle_cnt / 2;
|
||
|
uMgLost_R = 0;
|
||
|
}
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
} else if (Bound_Cnt_F == 0) {
|
||
|
/* mode_7: rising edge only one
|
||
|
* bound (not full), falling no boundary
|
||
|
*/
|
||
|
cycle_cnt = 128;
|
||
|
pBdPrev = &(pBdInfo_R->bd_info[0]);
|
||
|
if (pBdPrev->Bound_Start == 0) {
|
||
|
uDlySel_F = 0;
|
||
|
uDlySel_R = 63;
|
||
|
} else if (pBdPrev->Bound_End == 63) {
|
||
|
uDlySel_F = 63;
|
||
|
uDlySel_R = 0xFF;
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
uMgLost_F = 0xFF;
|
||
|
uMgLost_R = 0xFF;
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK]Warn: 1T > %d\r\n",
|
||
|
cycle_cnt);
|
||
|
}
|
||
|
} else if (Bound_Cnt_R == 0) {
|
||
|
/* Rising Edge No Boundary found */
|
||
|
if (Bound_Cnt_F > 1) {
|
||
|
/* falling edge not allowed two
|
||
|
* boundary Exist for this case
|
||
|
* this is a corner case,
|
||
|
*rising boundary may scan miss.
|
||
|
* oooooooooooooooooo
|
||
|
* oooxooooooooxooooo
|
||
|
*/
|
||
|
pInfo->cycle_cnt =
|
||
|
(pBdInfo_F->bd_info[1].Bound_End
|
||
|
+ pBdInfo_F->bd_info[1].Bound_Start) / 2
|
||
|
- (pBdInfo_F->bd_info[0].Bound_End
|
||
|
+ pBdInfo_F->bd_info[0].Bound_Start) / 2;
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt =
|
||
|
(pBdInfo_F->bd_info[0].Bound_End
|
||
|
+ pBdInfo_F->bd_info[0].Bound_Start) / 2;
|
||
|
return ret;
|
||
|
} else if (Bound_Cnt_F > 0) {
|
||
|
/* mode_8: falling edge
|
||
|
* have one Boundary exist
|
||
|
*/
|
||
|
pBdPrev = &(pBdInfo_F->bd_info[0]);
|
||
|
|
||
|
/* this boundary is full bound */
|
||
|
if (pBdPrev->is_fullbound) {
|
||
|
uBD_mid_prev =
|
||
|
(pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
|
||
|
if (pBdPrev->Bound_End >
|
||
|
(64 - pBdPrev->Bound_Start))
|
||
|
cycle_cnt =
|
||
|
2 *
|
||
|
(pBdPrev->Bound_End + 1);
|
||
|
else
|
||
|
cycle_cnt =
|
||
|
2 *
|
||
|
(64 - pBdPrev->Bound_Start);
|
||
|
|
||
|
uDlySel_R = uBD_mid_prev;
|
||
|
uMgLost_R = 0xFF;
|
||
|
uDlySel_F = 0xFF;
|
||
|
uMgLost_F = 0xFF;
|
||
|
} else {
|
||
|
cycle_cnt = 128;
|
||
|
|
||
|
uDlySel_R =
|
||
|
(pBdPrev->Bound_Start ==
|
||
|
0) ? 0 : 63;
|
||
|
uMgLost_R = 0xFF;
|
||
|
uDlySel_F = 0xFF;
|
||
|
uMgLost_F = 0xFF;
|
||
|
}
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK]Warning: 1T > %d\r\n",
|
||
|
cycle_cnt);
|
||
|
} else {
|
||
|
/* falling edge no boundary
|
||
|
* exist no need tuning
|
||
|
*/
|
||
|
cycle_cnt = 128;
|
||
|
uDlySel_F = 0;
|
||
|
uMgLost_F = 0xFF;
|
||
|
uDlySel_R = 0;
|
||
|
uMgLost_R = 0xFF;
|
||
|
AUTOK_RAWPRINT("[AUTOK]Warn: 1T > %d\r\n",
|
||
|
cycle_cnt);
|
||
|
}
|
||
|
} else {
|
||
|
/* Error if bound_cnt > 3 there must
|
||
|
*be at least one full boundary exist
|
||
|
*/
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* warn if bd count>4 (current hw design,
|
||
|
*this case cannot happen)
|
||
|
*/
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Select Optimised Sample edge & delay count (the small one) */
|
||
|
pInfo->cycle_cnt = cycle_cnt;
|
||
|
if (uDlySel_R <= uDlySel_F) {
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt = uDlySel_R;
|
||
|
} else {
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
pInfo->opt_dly_cnt = uDlySel_F;
|
||
|
|
||
|
}
|
||
|
AUTOK_RAWPRINT("[AUTOK]Analysis Result: 1T = %d\r\n", cycle_cnt);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#if SINGLE_EDGE_ONLINE_TUNE
|
||
|
static int
|
||
|
autok_pad_dly_sel_single_edge(struct AUTOK_SCAN_RES *pInfo,
|
||
|
unsigned int cycle_cnt_ref, unsigned int *pDlySel)
|
||
|
{
|
||
|
/* Save the first boundary info for calc optimised dly count */
|
||
|
struct BOUND_INFO *pBdPrev = NULL;
|
||
|
/* Save the second boundary
|
||
|
* info for calc optimised dly count
|
||
|
*/
|
||
|
struct BOUND_INFO *pBdNext = NULL;
|
||
|
unsigned int Bound_Cnt = 0;
|
||
|
unsigned int uBD_mid_prev = 0;
|
||
|
int uDlySel = 0;
|
||
|
int uMgLost = 0;
|
||
|
unsigned int ret = 0;
|
||
|
|
||
|
Bound_Cnt = pInfo->bd_cnt;
|
||
|
if (Bound_Cnt > 1) {
|
||
|
pBdPrev = &(pInfo->bd_info[0]);
|
||
|
pBdNext = &(pInfo->bd_info[1]);
|
||
|
if (!(pBdPrev->is_fullbound)) {
|
||
|
/* mode_1: at least 2 Bound and Boud0_Start == 0 */
|
||
|
uDlySel = (pBdPrev->Bound_End +
|
||
|
pBdNext->Bound_Start) / 2;
|
||
|
uMgLost = (uDlySel > 31) ? (uDlySel - 31) : 0;
|
||
|
uDlySel = (uDlySel > 31) ? 31 : uDlySel;
|
||
|
|
||
|
} else {
|
||
|
/* mode_2: at least 2 Bound
|
||
|
* found and Bound0_Start != 0
|
||
|
*/
|
||
|
uBD_mid_prev = (pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
if (uBD_mid_prev >= cycle_cnt_ref / 2) {
|
||
|
uDlySel = uBD_mid_prev - cycle_cnt_ref / 2;
|
||
|
uMgLost = 0;
|
||
|
} else if (cycle_cnt_ref / 2 - uBD_mid_prev <
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
uDlySel = 0;
|
||
|
uMgLost = cycle_cnt_ref / 2 - uBD_mid_prev;
|
||
|
} else {
|
||
|
uDlySel = (pBdPrev->Bound_End +
|
||
|
pBdNext->Bound_Start) / 2;
|
||
|
if ((uDlySel > 31)
|
||
|
&& (uDlySel - 31 <
|
||
|
AUTOK_MARGIN_THOLD)) {
|
||
|
uDlySel = 31;
|
||
|
uMgLost = uDlySel - 31;
|
||
|
} else {
|
||
|
/* uDlySel = uDlySel; */
|
||
|
uMgLost = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (Bound_Cnt > 0) {
|
||
|
/* only one bound fond */
|
||
|
pBdPrev = &(pInfo->bd_info[0]);
|
||
|
if (pBdPrev->is_fullbound) {
|
||
|
/* mode_3: Bound_S != 0 */
|
||
|
uBD_mid_prev = (pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
if (uBD_mid_prev >= cycle_cnt_ref / 2) {
|
||
|
uDlySel = uBD_mid_prev - cycle_cnt_ref / 2;
|
||
|
uMgLost = 0;
|
||
|
} else if (cycle_cnt_ref / 2 - uBD_mid_prev <
|
||
|
AUTOK_MARGIN_THOLD) {
|
||
|
uDlySel = 0;
|
||
|
uMgLost = cycle_cnt_ref / 2 - uBD_mid_prev;
|
||
|
} else if ((uBD_mid_prev > 31 - AUTOK_MARGIN_THOLD)
|
||
|
|| (pBdPrev->Bound_Start >= 16)) {
|
||
|
uDlySel = 0;
|
||
|
uMgLost = cycle_cnt_ref / 2 - uBD_mid_prev;
|
||
|
} else if (uBD_mid_prev + cycle_cnt_ref / 2 <= 63) {
|
||
|
/* Left Margin not enough must
|
||
|
* need to select the right side
|
||
|
*/
|
||
|
uDlySel = uBD_mid_prev + cycle_cnt_ref / 2;
|
||
|
uMgLost = 0;
|
||
|
} else {
|
||
|
uDlySel = 63;
|
||
|
uMgLost = uBD_mid_prev + cycle_cnt_ref / 2 - 63;
|
||
|
}
|
||
|
} else if (pBdPrev->Bound_Start == 0) {
|
||
|
/* mode_4 : Only one Boud and
|
||
|
* Boud_S = 0 (Currently 1T nearly equal 64 )
|
||
|
*/
|
||
|
|
||
|
/* May not exactly by for
|
||
|
* Cycle_Cnt enough can don't care
|
||
|
*/
|
||
|
uBD_mid_prev = (pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
if (pBdPrev->Bound_Start + cycle_cnt_ref / 2 >= 31) {
|
||
|
uDlySel = 31;
|
||
|
uMgLost = uBD_mid_prev + cycle_cnt_ref / 2 - 31;
|
||
|
} else {
|
||
|
uDlySel = uBD_mid_prev + cycle_cnt_ref / 2;
|
||
|
uMgLost = 0;
|
||
|
}
|
||
|
} else {
|
||
|
/* mode_5: Only one Boud and Boud_E = 64
|
||
|
* May not exactly by for
|
||
|
* Cycle_Cnt enough can don't care
|
||
|
*/
|
||
|
uBD_mid_prev = (pBdPrev->Bound_Start +
|
||
|
pBdPrev->Bound_End) / 2;
|
||
|
if (pBdPrev->Bound_Start < cycle_cnt_ref / 2) {
|
||
|
uDlySel = 0;
|
||
|
uMgLost = cycle_cnt_ref / 2 - uBD_mid_prev;
|
||
|
} else if (uBD_mid_prev - cycle_cnt_ref / 2 > 31) {
|
||
|
uDlySel = 31;
|
||
|
uMgLost = uBD_mid_prev - cycle_cnt_ref / 2 - 31;
|
||
|
} else {
|
||
|
uDlySel = uBD_mid_prev - cycle_cnt_ref / 2;
|
||
|
uMgLost = 0;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/*mode_6: no bound foud */
|
||
|
uDlySel = 31;
|
||
|
uMgLost = 0xFF;
|
||
|
}
|
||
|
*pDlySel = uDlySel;
|
||
|
if (uDlySel > 31) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Warn Dly %d>31 easily effected by Voltage\r\n",
|
||
|
uDlySel);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int autok_ds_dly_sel(struct AUTOK_SCAN_RES_NEW *pInfo,
|
||
|
unsigned int *pDlySel)
|
||
|
{
|
||
|
int uDlySel = 0;
|
||
|
unsigned int max_pass_win;
|
||
|
unsigned char max_pass_win_position;
|
||
|
unsigned char i;
|
||
|
|
||
|
max_pass_win_position = 0;
|
||
|
max_pass_win = pInfo->pass_info[0].bd_e
|
||
|
- pInfo->pass_info[0].bd_s;
|
||
|
|
||
|
for (i = 0; i < pInfo->pass_cnt; i++)
|
||
|
if ((pInfo->pass_info[i].bd_e
|
||
|
- pInfo->pass_info[i].bd_s) > max_pass_win) {
|
||
|
max_pass_win = pInfo->pass_info[i].bd_e
|
||
|
- pInfo->pass_info[i].bd_s;
|
||
|
max_pass_win_position = i;
|
||
|
}
|
||
|
uDlySel =
|
||
|
(pInfo->pass_info[max_pass_win_position].bd_s
|
||
|
+ pInfo->pass_info[max_pass_win_position].bd_e) / 2;
|
||
|
*pDlySel = uDlySel;
|
||
|
|
||
|
return max_pass_win;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************
|
||
|
* FUNCTION
|
||
|
* autok_adjust_param
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* This function for auto-K, adjust msdc parameter
|
||
|
*
|
||
|
* PARAMETERS
|
||
|
* host: msdc host manipulator pointer
|
||
|
* param: enum of msdc parameter
|
||
|
* value: value of msdc parameter
|
||
|
* rw: AUTOK_READ/AUTOK_WRITE
|
||
|
*
|
||
|
* RETURN VALUES
|
||
|
* error code: 0 success,
|
||
|
* -1 parameter input error
|
||
|
* -2 read/write fail
|
||
|
* -3 else error
|
||
|
*************************************************************************/
|
||
|
static int autok_adjust_param(struct msdc_host *host,
|
||
|
enum AUTOK_PARAM param, u32 *value, int rw)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
void __iomem *base_top = host->base_top;
|
||
|
#endif
|
||
|
u32 *reg;
|
||
|
u32 field = 0;
|
||
|
struct AUTOK_PLAT_TOP_CTRL platform_top_ctrl;
|
||
|
|
||
|
memset(&platform_top_ctrl, 0, sizeof(struct AUTOK_PLAT_TOP_CTRL));
|
||
|
get_platform_top_ctrl(platform_top_ctrl);
|
||
|
|
||
|
switch (param) {
|
||
|
case READ_DATA_SMPL_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) READ_DATA_SMPL_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
reg = (u32 *) MSDC_IOCON;
|
||
|
field = (u32) (MSDC_IOCON_R_D_SMPL_SEL);
|
||
|
break;
|
||
|
case WRITE_DATA_SMPL_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) WRITE_DATA_SMPL_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
reg = (u32 *) MSDC_IOCON;
|
||
|
field = (u32) (MSDC_IOCON_W_D_SMPL_SEL);
|
||
|
break;
|
||
|
case DATA_DLYLINE_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DATA_DLYLINE_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (DATA_K_VALUE_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_IOCON;
|
||
|
field = (u32) (MSDC_IOCON_DDLSEL);
|
||
|
}
|
||
|
break;
|
||
|
case MSDC_DAT_TUNE_SEL: /* 0-Dat tune 1-CLk tune ; */
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DAT_TUNE_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (PAD_RXDLY_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_RXDLYSEL);
|
||
|
}
|
||
|
break;
|
||
|
case MSDC_WCRC_ASYNC_FIFO_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) WCRC_ASYNC_FIFO_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT2;
|
||
|
field = (u32) (MSDC_PB2_CFGCRCSTS);
|
||
|
break;
|
||
|
case MSDC_RESP_ASYNC_FIFO_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) RESP_ASYNC_FIFO_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT2;
|
||
|
field = (u32) (MSDC_PB2_CFGRESP);
|
||
|
break;
|
||
|
case CMD_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_IOCON;
|
||
|
field = (u32) (MSDC_IOCON_RSPL);
|
||
|
break;
|
||
|
case CMD_FIFO_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_FIFO_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) EMMC50_CFG0;
|
||
|
field = (u32) (MSDC_EMMC50_CFG_CMD_EDGE_SEL);
|
||
|
break;
|
||
|
case RDATA_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) RDATA_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_IOCON;
|
||
|
field = (u32) (MSDC_IOCON_R_D_SMPL);
|
||
|
break;
|
||
|
case RD_FIFO_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) RD_FIFO_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT0;
|
||
|
field = (u32) (MSDC_PB0_RD_DAT_SEL);
|
||
|
break;
|
||
|
case WD_FIFO_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) WD_FIFO_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT2;
|
||
|
field = (u32) (MSDC_PB2_CFGCRCSTSEDGE);
|
||
|
break;
|
||
|
case CMD_RD_D_DLY1:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_RD_DLY out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CMD;
|
||
|
field = (u32) (PAD_CMD_RXDLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_CMDRDLY);
|
||
|
}
|
||
|
break;
|
||
|
case CMD_RD_D_DLY1_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_RD_DLY_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CMD;
|
||
|
field = (u32) (PAD_CMD_RD_RXDLY_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_CMDRRDLYSEL);
|
||
|
}
|
||
|
break;
|
||
|
case CMD_RD_D_DLY2:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_RD_DLY2 out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CMD;
|
||
|
field = (u32) (PAD_CMD_RXDLY2);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE1;
|
||
|
field = (u32) (MSDC_PAD_TUNE1_CMDRDLY2);
|
||
|
}
|
||
|
break;
|
||
|
case CMD_RD_D_DLY2_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_RD_DLY2_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CMD;
|
||
|
field = (u32) (PAD_CMD_RD_RXDLY2_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE1;
|
||
|
field = (u32) (MSDC_PAD_TUNE1_CMDRRDLY2SEL);
|
||
|
}
|
||
|
break;
|
||
|
case DAT_RD_D_DLY1:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DAT_RD_DLY out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (PAD_DAT_RD_RXDLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_DATRRDLY);
|
||
|
}
|
||
|
break;
|
||
|
case DAT_RD_D_DLY1_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DAT_RD_DLY_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (PAD_DAT_RD_RXDLY_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_DATRRDLYSEL);
|
||
|
}
|
||
|
break;
|
||
|
case DAT_RD_D_DLY2:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DAT_RD_DLY2 out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (PAD_DAT_RD_RXDLY2);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE1;
|
||
|
field = (u32) (MSDC_PAD_TUNE1_DATRRDLY2);
|
||
|
}
|
||
|
break;
|
||
|
case DAT_RD_D_DLY2_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) DAT_RD_DLY2_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (PAD_DAT_RD_RXDLY2_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE1;
|
||
|
field = (u32) (MSDC_PAD_TUNE1_DATRRDLY2SEL);
|
||
|
}
|
||
|
break;
|
||
|
case INT_DAT_LATCH_CK:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 7)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) INT_DAT_LATCH_CK out of range[0~7]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT0;
|
||
|
field = (u32) (MSDC_PB0_INT_DAT_LATCH_CK_SEL);
|
||
|
break;
|
||
|
case CKGEN_MSDC_DLY_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CKGEN_MSDC_DLY_SEL out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT0;
|
||
|
field = (u32) (MSDC_PB0_CKGEN_MSDC_DLY_SEL);
|
||
|
break;
|
||
|
case CMD_RSP_TA_CNTR:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 7)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) CMD_RSP_TA_CNTR out of range[0~7]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT1;
|
||
|
field = (u32) (MSDC_PB1_CMD_RSP_TA_CNTR);
|
||
|
break;
|
||
|
case WRDAT_CRCS_TA_CNTR:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 7)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) WRDAT_CRCS_TA_CNTR out of range[0~7]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) MSDC_PATCH_BIT1;
|
||
|
field = (u32) (MSDC_PB1_WRDAT_CRCS_TA_CNTR);
|
||
|
break;
|
||
|
case SDC_RX_ENHANCE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 7)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) SDC_RX_ENHANCE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (platform_top_ctrl.msdc0_rx_enhance_top == 1)
|
||
|
&& host->base_top)
|
||
|
|| ((host->hw->host_function == MSDC_SD)
|
||
|
&& (platform_top_ctrl.msdc1_rx_enhance_top == 1)
|
||
|
&& host->base_top)
|
||
|
|| ((host->hw->host_function == MSDC_SDIO)
|
||
|
&& (platform_top_ctrl.msdc2_rx_enhance_top == 1)
|
||
|
&& host->base_top)) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CONTROL;
|
||
|
field = (u32) (AUTOK_TOP_SDC_RX_ENHANCE_EN);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) SDC_ADV_CFG0;
|
||
|
field = (u32) (AUTOK_SDC_RX_ENH_EN);
|
||
|
}
|
||
|
break;
|
||
|
case PAD_CLK_TXDLY_AUTOK:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) PAD_CLK_TXDLY out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_CTL0;
|
||
|
field = (u32) (PAD_CLK_TXDLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) MSDC_PAD_TUNE0;
|
||
|
field = (u32) (MSDC_PAD_TUNE0_CLKTXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_WDATA_MUX_EN:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_WDATA_MUX_EN out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) EMMC50_CFG0;
|
||
|
field = (u32) (MSDC_EMMC50_CFG_CRC_STS_SEL);
|
||
|
break;
|
||
|
case EMMC50_CMD_MUX_EN:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_CMD_MUX_EN out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) EMMC50_CFG0;
|
||
|
field = (u32) (MSDC_EMMC50_CFG_CMD_RESP_SEL);
|
||
|
break;
|
||
|
case EMMC50_CMD_RESP_LATCH:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_CMD_LATCH out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) EMMC50_CFG0;
|
||
|
field = (u32) (MSDC_EMMC50_CFG_PADCMD_LATCHCK);
|
||
|
break;
|
||
|
case EMMC50_WDATA_EDGE:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_WDATA_EDGE out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
reg = (u32 *) EMMC50_CFG0;
|
||
|
field = (u32) (MSDC_EMMC50_CFG_CRC_STS_EDGE);
|
||
|
break;
|
||
|
case EMMC50_DS_Z_DLY1:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DS_Z_DLY1 out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (PAD_DS_DLY1);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DS_TUNE_DLY1);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DS_Z_DLY1_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DS1_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (PAD_DS_DLY_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DS_TUNE_DLYSEL);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DS_Z_DLY2:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DS_Z_DLY2 out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (PAD_DS_DLY2);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DS_TUNE_DLY2);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DS_Z_DLY2_SEL:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 1)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DS2_SEL out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (PAD_DS_DLY2_SEL);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DS_TUNE_DLY2SEL);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DS_ZDLY_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DS_Z_DLY3 out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (PAD_DS_DLY3);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DS_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DS_TUNE_DLY3);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_CMD_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_CMD_TX_DLY out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) EMMC_TOP_CMD;
|
||
|
field = (u32) (PAD_CMD_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_CMD_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_CMD_TUNE_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA0_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA0_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT0_TUNE;
|
||
|
field = (u32) (PAD_DAT0_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT01_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT0_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA1_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA1_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT1_TUNE;
|
||
|
field = (u32) (PAD_DAT1_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT01_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT1_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA2_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA2_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT2_TUNE;
|
||
|
field = (u32) (PAD_DAT2_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT23_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT2_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA3_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA3_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if (host->base_top) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT3_TUNE;
|
||
|
field = (u32) (PAD_DAT3_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT23_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT3_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA4_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA4_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (host->base_top)) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT4_TUNE;
|
||
|
field = (u32) (PAD_DAT4_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT45_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT4_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA5_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA5_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (host->base_top)) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT5_TUNE;
|
||
|
field = (u32) (PAD_DAT5_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT45_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT5_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA6_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA6_TX out of range[0~31]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (host->base_top)) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT6_TUNE;
|
||
|
field = (u32) (PAD_DAT6_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT67_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT6_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
case EMMC50_DATA7_TX_DLY:
|
||
|
if ((rw == AUTOK_WRITE) && (*value > 31)) {
|
||
|
pr_debug
|
||
|
("[%s](%d) EMMC50_DATA7_TX out of range[0~1]\n",
|
||
|
__func__, *value);
|
||
|
return -1;
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (host->base_top)) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
reg = (u32 *) TOP_EMMC50_PAD_DAT7_TUNE;
|
||
|
field = (u32) (PAD_DAT7_TX_DLY);
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
reg = (u32 *) EMMC50_PAD_DAT67_TUNE;
|
||
|
field = (u32) (MSDC_EMMC50_PAD_DAT7_TXDLY);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
pr_debug
|
||
|
("[%s] Value of [AUTOK_PARAM] is wrong\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (rw == AUTOK_READ)
|
||
|
MSDC_GET_FIELD(reg, field, *value);
|
||
|
else if (rw == AUTOK_WRITE) {
|
||
|
MSDC_SET_FIELD(reg, field, *value);
|
||
|
|
||
|
if (param == CKGEN_MSDC_DLY_SEL)
|
||
|
mdelay(1);
|
||
|
} else {
|
||
|
pr_debug("[%s] Value of [int rw] is wrong\n", __func__);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_param_update(enum AUTOK_PARAM param_id,
|
||
|
unsigned int result, u8 *autok_tune_res)
|
||
|
{
|
||
|
if (param_id < TUNING_PARAM_COUNT) {
|
||
|
if ((result > autok_param_info[param_id].range.end) ||
|
||
|
(result < autok_param_info[param_id].range.start)) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]param outof range : %d not in [%d,%d]\r\n",
|
||
|
result,
|
||
|
autok_param_info[param_id].range.start,
|
||
|
autok_param_info[param_id].range.end);
|
||
|
return -1;
|
||
|
}
|
||
|
autok_tune_res[param_id] = (u8) result;
|
||
|
return 0;
|
||
|
}
|
||
|
AUTOK_RAWPRINT("[AUTOK]param not found\r\n");
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int autok_param_apply(struct msdc_host *host, u8 *autok_tune_res)
|
||
|
{
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
value = (u8) autok_tune_res[i];
|
||
|
autok_adjust_param(host, i, &value, AUTOK_WRITE);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_result_dump(struct msdc_host *host, u8 *autok_tune_res)
|
||
|
{
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]CMD [EDGE:%d CMD_FIFO_EDGE:%d DLY1:%d DLY2:%d]\r\n",
|
||
|
autok_tune_res[0], autok_tune_res[1],
|
||
|
autok_tune_res[5], autok_tune_res[7]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DAT [RDAT_EDGE:%d RD_FIFO_EDGE:%d WD_FIFO_EDGE:%d]\r\n",
|
||
|
autok_tune_res[2], autok_tune_res[3],
|
||
|
autok_tune_res[4]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DAT [LATCH_CK:%d DLY1:%d DLY2:%d]\r\n",
|
||
|
autok_tune_res[13], autok_tune_res[9],
|
||
|
autok_tune_res[11]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DS [DLY1:%d DLY2:%d DLY3:%d]\r\n",
|
||
|
autok_tune_res[14], autok_tune_res[16],
|
||
|
autok_tune_res[18]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]CLK TX [%d]\r\n", autok_tune_res[28]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]CMD TX [%d]\r\n", autok_tune_res[19]);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D0:%d D1:%d D2:%d D3:%d]\r\n",
|
||
|
autok_tune_res[20], autok_tune_res[21],
|
||
|
autok_tune_res[22], autok_tune_res[23]);
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D4:%d D5:%d D6:%d D7:%d]\r\n",
|
||
|
autok_tune_res[24], autok_tune_res[25],
|
||
|
autok_tune_res[26], autok_tune_res[27]);
|
||
|
} else {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D0:%d D1:%d D2:%d D3:%d]\r\n",
|
||
|
autok_tune_res[20], autok_tune_res[21],
|
||
|
autok_tune_res[22], autok_tune_res[23]);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if AUTOK_PARAM_DUMP_ENABLE
|
||
|
static int autok_register_dump(struct msdc_host *host)
|
||
|
{
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
u8 autok_tune_res[TUNING_PARAM_COUNT];
|
||
|
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
autok_adjust_param(host, i, &value, AUTOK_READ);
|
||
|
autok_tune_res[i] = value;
|
||
|
}
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]CMD [EDGE:%d CMD_FIFO_EDGE:%d DLY1:%d DLY2:%d]\r\n",
|
||
|
autok_tune_res[0], autok_tune_res[1],
|
||
|
autok_tune_res[5], autok_tune_res[7]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DAT [RDAT_EDGE:%d RD_FIFO_EDGE:%d WD_FIFO_EDGE:%d]\r\n",
|
||
|
autok_tune_res[2], autok_tune_res[3],
|
||
|
autok_tune_res[4]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DAT [LATCH_CK:%d DLY1:%d DLY2:%d]\r\n",
|
||
|
autok_tune_res[13], autok_tune_res[9],
|
||
|
autok_tune_res[11]);
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]DS [DLY1:%d DLY2:%d DLY3:%d]\r\n",
|
||
|
autok_tune_res[14], autok_tune_res[16],
|
||
|
autok_tune_res[18]);
|
||
|
AUTOK_RAWPRINT("[AUTOK]CLK TX [%d]\r\n", autok_tune_res[28]);
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD TX [%d]\r\n", autok_tune_res[19]);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D0:%d D1:%d D2:%d D3:%d]\r\n",
|
||
|
autok_tune_res[20], autok_tune_res[21],
|
||
|
autok_tune_res[22], autok_tune_res[23]);
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D4:%d D5:%d D6:%d D7:%d]\r\n",
|
||
|
autok_tune_res[24], autok_tune_res[25],
|
||
|
autok_tune_res[26], autok_tune_res[27]);
|
||
|
} else {
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX [D0:%d D1:%d D2:%d D3:%d]\r\n",
|
||
|
autok_tune_res[20], autok_tune_res[21],
|
||
|
autok_tune_res[22], autok_tune_res[23]);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void autok_tuning_parameter_init(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
unsigned int ret = 0;
|
||
|
/* void __iomem *base = host->base; */
|
||
|
|
||
|
/* MSDC_SET_FIELD(MSDC_PATCH_BIT2, 7<<29, 2); */
|
||
|
/* MSDC_SET_FIELD(MSDC_PATCH_BIT2, 7<<16, 4); */
|
||
|
|
||
|
ret = autok_param_apply(host, res);
|
||
|
}
|
||
|
|
||
|
/*******************************************************
|
||
|
* Function: autok_adjust_paddly *
|
||
|
* Param : value - delay cnt from 0 to 63 *
|
||
|
* pad_sel - 0 for cmd pad and 1 for data pad *
|
||
|
*******************************************************/
|
||
|
#define CMD_PAD_RDLY 0
|
||
|
#define DAT_PAD_RDLY 1
|
||
|
#define DS_PAD_RDLY 2
|
||
|
static void autok_adjust_paddly(struct msdc_host *host,
|
||
|
unsigned int *value, unsigned int pad_sel)
|
||
|
{
|
||
|
unsigned int uCfgL = 0;
|
||
|
unsigned int uCfgLSel = 0;
|
||
|
unsigned int uCfgH = 0;
|
||
|
unsigned int uCfgHSel = 0;
|
||
|
unsigned int dly_cnt = *value;
|
||
|
|
||
|
uCfgL = (dly_cnt > 31) ? (31) : dly_cnt;
|
||
|
uCfgH = (dly_cnt > 31) ? (dly_cnt - 32) : 0;
|
||
|
|
||
|
uCfgLSel = (uCfgL > 0) ? 1 : 0;
|
||
|
uCfgHSel = (uCfgH > 0) ? 1 : 0;
|
||
|
switch (pad_sel) {
|
||
|
case CMD_PAD_RDLY:
|
||
|
autok_adjust_param(host, CMD_RD_D_DLY1,
|
||
|
&uCfgL, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, CMD_RD_D_DLY2,
|
||
|
&uCfgH, AUTOK_WRITE);
|
||
|
|
||
|
autok_adjust_param(host, CMD_RD_D_DLY1_SEL,
|
||
|
&uCfgLSel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, CMD_RD_D_DLY2_SEL,
|
||
|
&uCfgHSel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case DAT_PAD_RDLY:
|
||
|
autok_adjust_param(host, DAT_RD_D_DLY1,
|
||
|
&uCfgL, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, DAT_RD_D_DLY2,
|
||
|
&uCfgH, AUTOK_WRITE);
|
||
|
|
||
|
autok_adjust_param(host, DAT_RD_D_DLY1_SEL,
|
||
|
&uCfgLSel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, DAT_RD_D_DLY2_SEL,
|
||
|
&uCfgHSel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case DS_PAD_RDLY:
|
||
|
autok_adjust_param(host, EMMC50_DS_Z_DLY1,
|
||
|
&uCfgL, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DS_Z_DLY2,
|
||
|
&uCfgH, AUTOK_WRITE);
|
||
|
|
||
|
autok_adjust_param(host, EMMC50_DS_Z_DLY1_SEL,
|
||
|
&uCfgLSel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DS_Z_DLY2_SEL,
|
||
|
&uCfgHSel, AUTOK_WRITE);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void autok_paddly_update(unsigned int pad_sel,
|
||
|
unsigned int dly_cnt, u8 *autok_tune_res)
|
||
|
{
|
||
|
unsigned int uCfgL = 0;
|
||
|
unsigned int uCfgLSel = 0;
|
||
|
unsigned int uCfgH = 0;
|
||
|
unsigned int uCfgHSel = 0;
|
||
|
|
||
|
uCfgL = (dly_cnt > 31) ? (31) : dly_cnt;
|
||
|
uCfgH = (dly_cnt > 31) ? (dly_cnt - 32) : 0;
|
||
|
|
||
|
uCfgLSel = (uCfgL > 0) ? 1 : 0;
|
||
|
uCfgHSel = (uCfgH > 0) ? 1 : 0;
|
||
|
switch (pad_sel) {
|
||
|
case CMD_PAD_RDLY:
|
||
|
autok_param_update(CMD_RD_D_DLY1,
|
||
|
uCfgL, autok_tune_res);
|
||
|
autok_param_update(CMD_RD_D_DLY2,
|
||
|
uCfgH, autok_tune_res);
|
||
|
|
||
|
autok_param_update(CMD_RD_D_DLY1_SEL,
|
||
|
uCfgLSel, autok_tune_res);
|
||
|
autok_param_update(CMD_RD_D_DLY2_SEL,
|
||
|
uCfgHSel, autok_tune_res);
|
||
|
break;
|
||
|
case DAT_PAD_RDLY:
|
||
|
autok_param_update(DAT_RD_D_DLY1,
|
||
|
uCfgL, autok_tune_res);
|
||
|
autok_param_update(DAT_RD_D_DLY2,
|
||
|
uCfgH, autok_tune_res);
|
||
|
|
||
|
autok_param_update(DAT_RD_D_DLY1_SEL,
|
||
|
uCfgLSel, autok_tune_res);
|
||
|
autok_param_update(DAT_RD_D_DLY2_SEL,
|
||
|
uCfgHSel, autok_tune_res);
|
||
|
break;
|
||
|
case DS_PAD_RDLY:
|
||
|
autok_param_update(EMMC50_DS_Z_DLY1,
|
||
|
uCfgL, autok_tune_res);
|
||
|
autok_param_update(EMMC50_DS_Z_DLY2,
|
||
|
uCfgH, autok_tune_res);
|
||
|
|
||
|
autok_param_update(EMMC50_DS_Z_DLY1_SEL,
|
||
|
uCfgLSel, autok_tune_res);
|
||
|
autok_param_update(EMMC50_DS_Z_DLY2_SEL,
|
||
|
uCfgHSel, autok_tune_res);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void autok_window_apply(enum AUTOK_SCAN_WIN scan_win,
|
||
|
u64 sacn_window, unsigned char *autok_tune_res)
|
||
|
{
|
||
|
switch (scan_win) {
|
||
|
case CMD_RISE:
|
||
|
autok_tune_res[CMD_SCAN_R0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_R7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case CMD_FALL:
|
||
|
autok_tune_res[CMD_SCAN_F0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[CMD_SCAN_F7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case DAT_RISE:
|
||
|
autok_tune_res[DAT_SCAN_R0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_R7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case DAT_FALL:
|
||
|
autok_tune_res[DAT_SCAN_F0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[DAT_SCAN_F7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case DS_CMD_WIN:
|
||
|
autok_tune_res[DS_CMD_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[DS_CMD_SCAN_7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case DS_DATA_WIN:
|
||
|
autok_tune_res[DS_DAT_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_4] = (sacn_window >> 32) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_5] = (sacn_window >> 40) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_6] = (sacn_window >> 48) & 0xff;
|
||
|
autok_tune_res[DS_DAT_SCAN_7] = (sacn_window >> 56) & 0xff;
|
||
|
break;
|
||
|
case D_CMD_RX:
|
||
|
autok_tune_res[D_CMD_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[D_CMD_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[D_CMD_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[D_CMD_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
break;
|
||
|
case D_DATA_RX:
|
||
|
autok_tune_res[D_DATA_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[D_DATA_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[D_DATA_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[D_DATA_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
break;
|
||
|
case H_CMD_TX:
|
||
|
autok_tune_res[H_CMD_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[H_CMD_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[H_CMD_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[H_CMD_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
break;
|
||
|
case H_DATA_TX:
|
||
|
autok_tune_res[H_DATA_SCAN_0] = (sacn_window >> 0) & 0xff;
|
||
|
autok_tune_res[H_DATA_SCAN_1] = (sacn_window >> 8) & 0xff;
|
||
|
autok_tune_res[H_DATA_SCAN_2] = (sacn_window >> 16) & 0xff;
|
||
|
autok_tune_res[H_DATA_SCAN_3] = (sacn_window >> 24) & 0xff;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void msdc_autok_version_apply(unsigned char *autok_tune_res)
|
||
|
{
|
||
|
autok_tune_res[AUTOK_VER0] = (AUTOK_VERSION >> 0) & 0xff;
|
||
|
autok_tune_res[AUTOK_VER1] = (AUTOK_VERSION >> 8) & 0xff;
|
||
|
autok_tune_res[AUTOK_VER2] = (AUTOK_VERSION >> 16) & 0xff;
|
||
|
autok_tune_res[AUTOK_VER3] = (AUTOK_VERSION >> 24) & 0xff;
|
||
|
}
|
||
|
|
||
|
/*******************************************************
|
||
|
* Exectue tuning IF Implenment *
|
||
|
*******************************************************/
|
||
|
static int autok_write_param(struct msdc_host *host,
|
||
|
enum AUTOK_PARAM param, u32 value)
|
||
|
{
|
||
|
autok_adjust_param(host, param, &value, AUTOK_WRITE);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int autok_path_sel(struct msdc_host *host)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
void __iomem *base_top = host->base_top;
|
||
|
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
struct AUTOK_PLAT_TOP_CTRL platform_top_ctrl;
|
||
|
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
memset(&platform_top_ctrl, 0, sizeof(struct AUTOK_PLAT_TOP_CTRL));
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_func(platform_para_func);
|
||
|
get_platform_top_ctrl(platform_top_ctrl);
|
||
|
|
||
|
autok_write_param(host, READ_DATA_SMPL_SEL, 0);
|
||
|
autok_write_param(host, WRITE_DATA_SMPL_SEL, 0);
|
||
|
|
||
|
/* clK tune all data Line share dly */
|
||
|
autok_write_param(host, DATA_DLYLINE_SEL, 0);
|
||
|
|
||
|
/* data tune mode select */
|
||
|
#if CHIP_DENALI_3_DAT_TUNE
|
||
|
autok_write_param(host, MSDC_DAT_TUNE_SEL, 1);
|
||
|
#else
|
||
|
autok_write_param(host, MSDC_DAT_TUNE_SEL, 0);
|
||
|
#endif
|
||
|
autok_write_param(host, MSDC_WCRC_ASYNC_FIFO_SEL, 1);
|
||
|
autok_write_param(host, MSDC_RESP_ASYNC_FIFO_SEL, 0);
|
||
|
|
||
|
/* eMMC50 Function Mux */
|
||
|
/* write path switch to emmc45 */
|
||
|
autok_write_param(host, EMMC50_WDATA_MUX_EN, 0);
|
||
|
|
||
|
/* response path switch to emmc45 */
|
||
|
autok_write_param(host, EMMC50_CMD_MUX_EN, 0);
|
||
|
/* response use DS latch */
|
||
|
autok_write_param(host, EMMC50_CMD_RESP_LATCH, 0);
|
||
|
autok_write_param(host, EMMC50_WDATA_EDGE, 0);
|
||
|
MSDC_SET_FIELD(EMMC50_CFG1, MSDC_EMMC50_CFG1_DSCFG, 0);
|
||
|
|
||
|
/* Common Setting Config */
|
||
|
autok_write_param(host, CKGEN_MSDC_DLY_SEL,
|
||
|
platform_para_rx.ckgen_val);
|
||
|
autok_write_param(host, CMD_RSP_TA_CNTR,
|
||
|
platform_para_rx.cmd_ta_val);
|
||
|
autok_write_param(host, WRDAT_CRCS_TA_CNTR,
|
||
|
platform_para_rx.crc_ta_val);
|
||
|
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_GET_BUSY_MA,
|
||
|
platform_para_rx.busy_ma_val);
|
||
|
/* DDR50 byte swap issue design fix feature enable */
|
||
|
if (platform_para_func.ddr50_fix == 1)
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, 1 << 19, 1);
|
||
|
/* multi sync circuit design improve */
|
||
|
if (platform_para_func.multi_sync == 1) {
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_STOP_DLY_SEL, 3);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_POPENCNT, 8);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, 0x3 << 19, 3);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG, SDC_FIFO_CFG_WR_VALID_SEL, 0);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG, SDC_FIFO_CFG_RD_VALID_SEL, 0);
|
||
|
}
|
||
|
|
||
|
/* duty bypass that may influence timing */
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (platform_para_func.msdc0_bypass_duty_modify == 1)) {
|
||
|
if (host->base_top) {
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, DCC_SEL,
|
||
|
platform_para_tx.msdc0_duty_bypass);
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, HL_SEL,
|
||
|
platform_para_tx.msdc0_hl_duty_sel);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_CTL0,
|
||
|
MSDC_EMMC50_PAD_CTL0_DCCSEL,
|
||
|
platform_para_tx.msdc0_duty_bypass);
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_CTL0,
|
||
|
MSDC_EMMC50_PAD_CTL0_HLSEL,
|
||
|
platform_para_tx.msdc0_hl_duty_sel);
|
||
|
}
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_SD)
|
||
|
&& (platform_para_func.msdc1_bypass_duty_modify == 1)) {
|
||
|
if (host->base_top) {
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, DCC_SEL,
|
||
|
platform_para_tx.msdc1_duty_bypass);
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, HL_SEL,
|
||
|
platform_para_tx.msdc1_hl_duty_sel);
|
||
|
}
|
||
|
}
|
||
|
if ((host->hw->host_function == MSDC_SDIO)
|
||
|
&& (platform_para_func.msdc2_bypass_duty_modify == 1)) {
|
||
|
if (host->base_top) {
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, DCC_SEL,
|
||
|
platform_para_tx.msdc2_duty_bypass);
|
||
|
MSDC_SET_FIELD(TOP_EMMC50_PAD_CTL0, HL_SEL,
|
||
|
platform_para_tx.msdc2_hl_duty_sel);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_CTL0,
|
||
|
MSDC_EMMC50_PAD_CTL0_DCCSEL,
|
||
|
platform_para_tx.msdc2_duty_bypass);
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_CTL0,
|
||
|
MSDC_EMMC50_PAD_CTL0_HLSEL,
|
||
|
platform_para_tx.msdc2_hl_duty_sel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_path_sel);
|
||
|
|
||
|
int autok_init_ddr208(struct msdc_host *host)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_func(platform_para_func);
|
||
|
|
||
|
/* driver may miss data tune path setting in the interim */
|
||
|
autok_path_sel(host);
|
||
|
|
||
|
/* if any specific config need modify add here */
|
||
|
if (platform_para_func.rx_enhance) {
|
||
|
autok_write_param(host, SDC_RX_ENHANCE, 1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL, 0);
|
||
|
} else {
|
||
|
/* LATCH_TA_EN Config for WCRC Path non_HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_ddr208);
|
||
|
/* LATCH_TA_EN Config for CMD Path non_HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_ddr208);
|
||
|
}
|
||
|
/* response path switch to emmc50 */
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
autok_write_param(host, EMMC50_CMD_MUX_EN, 0);
|
||
|
autok_write_param(host, EMMC50_CMD_RESP_LATCH, 0);
|
||
|
#else
|
||
|
autok_write_param(host, EMMC50_CMD_MUX_EN, 1);
|
||
|
autok_write_param(host, EMMC50_CMD_RESP_LATCH, 1);
|
||
|
#endif
|
||
|
MSDC_SET_FIELD(EMMC50_CFG1, MSDC_EMMC50_CFG1_DSCFG, 1);
|
||
|
/* write path switch to emmc50 */
|
||
|
autok_write_param(host, EMMC50_WDATA_MUX_EN, 1);
|
||
|
/* Specifical for HS400 Path Sel */
|
||
|
autok_write_param(host, MSDC_WCRC_ASYNC_FIFO_SEL, 0);
|
||
|
if (platform_para_func.multi_sync == 0) {
|
||
|
if (platform_para_func.new_path_ddr208 == 1) {
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 0);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 0);
|
||
|
} else if (platform_para_func.new_path_ddr208 == 0) {
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 1);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 1);
|
||
|
}
|
||
|
}
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_READ_DAT_CNT,
|
||
|
platform_para_rx.read_dat_cnt_ddr208);
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_END_BIT_CHK_CNT,
|
||
|
platform_para_rx.end_bit_chk_cnt_ddr208);
|
||
|
MSDC_SET_FIELD(EMMC50_CFG1, MSDC_EMMC50_CFG1_CKSWITCH_CNT,
|
||
|
platform_para_rx.latchck_switch_cnt_ddr208);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_init_ddr208);
|
||
|
|
||
|
int autok_init_sdr104(struct msdc_host *host)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_func(platform_para_func);
|
||
|
|
||
|
/* driver may miss data tune path setting in the interim */
|
||
|
autok_path_sel(host);
|
||
|
|
||
|
/* if any specific config need modify add here */
|
||
|
if (platform_para_func.rx_enhance) {
|
||
|
autok_write_param(host, SDC_RX_ENHANCE, 1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL, 0);
|
||
|
} else {
|
||
|
if (host->sclk <= 100000000) {
|
||
|
/* LATCH_TA_EN Config for WCRC Path HS FS mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_hs);
|
||
|
/* LATCH_TA_EN Config for CMD Path HS FS mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_hs);
|
||
|
} else if (host->hw->host_function == MSDC_SD) {
|
||
|
/* LATCH_TA_EN Config for WCRC Path SDR104 mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_sd_sdr104);
|
||
|
/* LATCH_TA_EN Config for CMD Path SDR104 mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_sd_sdr104);
|
||
|
} else if (host->hw->host_function == MSDC_SDIO) {
|
||
|
/* LATCH_TA_EN Config for WCRC Path SDR104 mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_sdio_sdr104);
|
||
|
/* LATCH_TA_EN Config for CMD Path SDR104 mode */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_sdio_sdr104);
|
||
|
}
|
||
|
}
|
||
|
/* enable dvfs feature */
|
||
|
/* if (host->hw->host_function == MSDC_SDIO) */
|
||
|
/* MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, 1); */
|
||
|
if (platform_para_func.multi_sync == 0) {
|
||
|
if (platform_para_func.new_path_sdr104 == 1) {
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_STOP_DLY_SEL,
|
||
|
platform_para_rx.new_stop_sdr104);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_POPENCNT,
|
||
|
platform_para_rx.new_water_sdr104);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 0);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 0);
|
||
|
} else if (platform_para_func.new_path_sdr104 == 0) {
|
||
|
/* use default setting */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_STOP_DLY_SEL,
|
||
|
platform_para_rx.old_stop_sdr104);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_POPENCNT,
|
||
|
platform_para_rx.old_water_sdr104);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 1);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_init_sdr104);
|
||
|
|
||
|
int autok_init_hs200(struct msdc_host *host)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_func(platform_para_func);
|
||
|
|
||
|
/* driver may miss data tune path setting in the interim */
|
||
|
autok_path_sel(host);
|
||
|
|
||
|
/* if any specific config need modify add here */
|
||
|
if (platform_para_func.rx_enhance) {
|
||
|
autok_write_param(host, SDC_RX_ENHANCE, 1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL, 0);
|
||
|
} else {
|
||
|
/* LATCH_TA_EN Config for WCRC Path non_HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_hs200);
|
||
|
/* LATCH_TA_EN Config for CMD Path non_HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_hs200);
|
||
|
}
|
||
|
if (platform_para_func.multi_sync == 0) {
|
||
|
if (platform_para_func.new_path_hs200 == 1) {
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_STOP_DLY_SEL,
|
||
|
platform_para_rx.new_stop_hs200);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_POPENCNT,
|
||
|
platform_para_rx.new_water_hs200);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 0);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 0);
|
||
|
} else if (platform_para_func.new_path_hs200 == 0) {
|
||
|
/* use default setting */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT1, MSDC_PB1_STOP_DLY_SEL,
|
||
|
platform_para_rx.old_stop_hs200);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_POPENCNT,
|
||
|
platform_para_rx.old_water_hs200);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 1);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_init_hs200);
|
||
|
|
||
|
int autok_init_hs400(struct msdc_host *host)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_func(platform_para_func);
|
||
|
|
||
|
/* driver may miss data tune path setting in the interim */
|
||
|
autok_path_sel(host);
|
||
|
|
||
|
/* if any specific config need modify add here */
|
||
|
if (platform_para_func.rx_enhance) {
|
||
|
autok_write_param(host, SDC_RX_ENHANCE, 1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL, 0);
|
||
|
} else {
|
||
|
/* LATCH_TA_EN Config for WCRC Path HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_CRCSTSENSEL,
|
||
|
platform_para_rx.latch_en_crc_hs400);
|
||
|
/* LATCH_TA_EN Config for CMD Path HS400 */
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2, MSDC_PB2_RESPSTENSEL,
|
||
|
platform_para_rx.latch_en_cmd_hs400);
|
||
|
}
|
||
|
/* write path switch to emmc50 */
|
||
|
autok_write_param(host, EMMC50_WDATA_MUX_EN, 1);
|
||
|
/* Specifical for HS400 Path Sel */
|
||
|
autok_write_param(host, MSDC_WCRC_ASYNC_FIFO_SEL, 0);
|
||
|
if (platform_para_func.multi_sync == 0) {
|
||
|
if (platform_para_func.new_path_hs400 == 1) {
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 0);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 0);
|
||
|
} else if (platform_para_func.new_path_hs400 == 0) {
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_WR_VALID_SEL, 1);
|
||
|
MSDC_SET_FIELD(SDC_FIFO_CFG,
|
||
|
SDC_FIFO_CFG_RD_VALID_SEL, 1);
|
||
|
}
|
||
|
}
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_READ_DAT_CNT,
|
||
|
platform_para_rx.read_dat_cnt_hs400);
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0, MSDC_EMMC50_CFG_END_BIT_CHK_CNT,
|
||
|
platform_para_rx.end_bit_chk_cnt_hs400);
|
||
|
MSDC_SET_FIELD(EMMC50_CFG1, MSDC_EMMC50_CFG1_CKSWITCH_CNT,
|
||
|
platform_para_rx.latchck_switch_cnt_hs400);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_init_hs400);
|
||
|
|
||
|
int execute_online_tuning_hs400(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = 0;
|
||
|
int err = 0;
|
||
|
unsigned int response;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int score = 0;
|
||
|
unsigned int j, k, cycle_value;
|
||
|
struct AUTOK_REF_INFO *pBdInfo;
|
||
|
struct AUTOK_REF_INFO_NEW *pInfo;
|
||
|
char tune_result_str64[65];
|
||
|
u8 p_autok_tune_res[TUNING_PARA_SCAN_COUNT];
|
||
|
unsigned int opcode = MMC_SEND_STATUS;
|
||
|
unsigned int uDatDly = 0;
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
|
||
|
pBdInfo = kmalloc(sizeof(struct AUTOK_REF_INFO), GFP_ATOMIC);
|
||
|
if (!pBdInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
pInfo = kmalloc(sizeof(struct AUTOK_REF_INFO_NEW), GFP_ATOMIC);
|
||
|
if (!pInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
kfree(pBdInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
|
||
|
autok_init_hs400(host);
|
||
|
memset((void *)p_autok_tune_res, 0,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
|
||
|
/* restore TX value */
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.msdc0_hs400_clktx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_CMD_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_cmdtx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat0tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat1tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat2tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat3tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA4_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat4tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA5_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat5tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA6_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat6tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA7_TX_DLY,
|
||
|
platform_para_tx.msdc0_hs400_dat7tx, p_autok_tune_res);
|
||
|
/* Step1 : Tuning Cmd Path */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, CMD_EDGE, &uCmdEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
RawData64 |= (u64)(1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (uCmdEdge)
|
||
|
autok_window_apply(CMD_FALL,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(CMD_RISE,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uCmdEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(CMD_EDGE,
|
||
|
pBdInfo->opt_edge_sel, p_autok_tune_res);
|
||
|
autok_paddly_update(CMD_PAD_RDLY,
|
||
|
pBdInfo->opt_dly_cnt, p_autok_tune_res);
|
||
|
|
||
|
/* DLY3 keep default value 20 */
|
||
|
p_autok_tune_res[EMMC50_DS_ZDLY_DLY] = platform_para_rx.ds_dly3_hs400;
|
||
|
cycle_value = pBdInfo->cycle_cnt;
|
||
|
/* Step2 : Tuning DS Clk Path-ZCLK only tune DLY1 */
|
||
|
#ifdef CMDQ
|
||
|
opcode = MMC_SEND_EXT_CSD; /* can also use MMC_READ_SINGLE_BLOCK */
|
||
|
#else
|
||
|
opcode = MMC_READ_SINGLE_BLOCK;
|
||
|
#endif
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
/* check device status */
|
||
|
ret = autok_send_tune_cmd(host, MMC_SEND_STATUS, TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
if (ret == E_RES_PASS) {
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
AUTOK_RAWPRINT("[AUTOK]device status 0x%08x\r\n", response);
|
||
|
} else
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD err while check device status\r\n");
|
||
|
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
|
||
|
/* check QSR status when CQ on */
|
||
|
if (host->mmc->card && mmc_card_cmdq(host->mmc->card)) {
|
||
|
ret = autok_send_tune_cmd(host, CHECK_QSR,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if (ret == E_RES_PASS) {
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
AUTOK_RAWPRINT("[AUTOK]QSR 0x%08x\r\n", response);
|
||
|
} else
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD error while check QSR\r\n");
|
||
|
} else
|
||
|
AUTOK_RAWPRINT("[AUTOK]CQ not enabled\r\n");
|
||
|
#endif
|
||
|
/* tune data pad delay , find data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
autok_adjust_paddly(host, &j, DAT_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_DATA,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO | E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0)
|
||
|
break;
|
||
|
else if ((ret & E_RES_FATAL_ERR) != 0) {
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0) {
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1] = j;
|
||
|
if (j)
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1_SEL] = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
RawData64 = 0LL;
|
||
|
/* tune DS delay , base on data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
autok_adjust_paddly(host, &j, DS_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_DATA,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC
|
||
|
| E_RES_DAT_TMO)) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
RawData64 |= 0xffffffff00000000;
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DLY1/2 %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
autok_window_apply(DS_DATA_WIN, RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64_new(RawData64, &pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &uDatDly);
|
||
|
autok_paddly_update(DS_PAD_RDLY, uDatDly, p_autok_tune_res);
|
||
|
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
autok_result_dump(host, p_autok_tune_res);
|
||
|
#if AUTOK_PARAM_DUMP_ENABLE
|
||
|
autok_register_dump(host);
|
||
|
#endif
|
||
|
msdc_autok_version_apply(p_autok_tune_res);
|
||
|
if (res != NULL) {
|
||
|
memcpy((void *)res, (void *)p_autok_tune_res,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
}
|
||
|
|
||
|
kfree(pBdInfo);
|
||
|
kfree(pInfo);
|
||
|
return 0;
|
||
|
fail:
|
||
|
kfree(pBdInfo);
|
||
|
kfree(pInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int execute_cmd_online_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
void __iomem *base_top = host->base_top;
|
||
|
#endif
|
||
|
unsigned int ret = 0;
|
||
|
int err = 0;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int score = 0;
|
||
|
unsigned int j, k; /* cycle_value */
|
||
|
struct AUTOK_REF_INFO *pBdInfo;
|
||
|
char tune_result_str64[65];
|
||
|
u8 p_autok_tune_res[5];
|
||
|
unsigned int opcode = MMC_SEND_STATUS;
|
||
|
struct autok_host autok_host_para;
|
||
|
|
||
|
pBdInfo = kmalloc(sizeof(struct AUTOK_REF_INFO), GFP_ATOMIC);
|
||
|
if (!pBdInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset((void *)p_autok_tune_res, 0,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
|
||
|
/* Tuning Cmd Path */
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, CMD_EDGE, &uCmdEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
RawData64 |= (u64)(1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uCmdEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_adjust_param(host, CMD_EDGE,
|
||
|
&pBdInfo->opt_edge_sel, AUTOK_WRITE);
|
||
|
autok_adjust_paddly(host,
|
||
|
&pBdInfo->opt_dly_cnt, CMD_PAD_RDLY);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL,
|
||
|
p_autok_tune_res[0]);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
#if !defined(FPGA_PLATFORM)
|
||
|
MSDC_GET_FIELD(EMMC_TOP_CMD,
|
||
|
PAD_CMD_RXDLY,
|
||
|
p_autok_tune_res[1]);
|
||
|
MSDC_GET_FIELD(EMMC_TOP_CMD,
|
||
|
PAD_CMD_RD_RXDLY_SEL,
|
||
|
p_autok_tune_res[2]);
|
||
|
MSDC_GET_FIELD(EMMC_TOP_CMD,
|
||
|
PAD_CMD_RXDLY2,
|
||
|
p_autok_tune_res[3]);
|
||
|
MSDC_GET_FIELD(EMMC_TOP_CMD,
|
||
|
PAD_CMD_RD_RXDLY2_SEL,
|
||
|
p_autok_tune_res[4]);
|
||
|
#else
|
||
|
kfree(pBdInfo);
|
||
|
return 0;
|
||
|
#endif
|
||
|
} else {
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE0,
|
||
|
MSDC_PAD_TUNE0_CMDRDLY,
|
||
|
p_autok_tune_res[1]);
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE0,
|
||
|
MSDC_PAD_TUNE0_CMDRRDLYSEL,
|
||
|
p_autok_tune_res[2]);
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE1,
|
||
|
MSDC_PAD_TUNE1_CMDRDLY2,
|
||
|
p_autok_tune_res[3]);
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE1,
|
||
|
MSDC_PAD_TUNE1_CMDRRDLY2SEL,
|
||
|
p_autok_tune_res[4]);
|
||
|
}
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD [EDGE:%d DLY1:%d DLY2:%d]\r\n",
|
||
|
p_autok_tune_res[0], p_autok_tune_res[1], p_autok_tune_res[3]);
|
||
|
|
||
|
if (res != NULL) {
|
||
|
memcpy((void *)res, (void *)p_autok_tune_res,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
}
|
||
|
|
||
|
kfree(pBdInfo);
|
||
|
return 0;
|
||
|
fail:
|
||
|
kfree(pBdInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* online tuning for latch ck */
|
||
|
int autok_tune_latch_ck(struct msdc_host *host, unsigned int opcode,
|
||
|
unsigned int latch_ck)
|
||
|
{
|
||
|
unsigned int ret = 0;
|
||
|
unsigned int j, k;
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int tune_time;
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_PARA_MISC platform_para_misc;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_misc, 0, sizeof(struct AUTOK_PLAT_PARA_MISC));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_para_misc(platform_para_misc);
|
||
|
get_platform_func(platform_para_func);
|
||
|
|
||
|
if (platform_para_func.fifo_1k == 1)
|
||
|
autok_host_para.fifo_tune = 1;
|
||
|
else
|
||
|
autok_host_para.fifo_tune = 0;
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
switch (host->hw->host_function) {
|
||
|
case MSDC_EMMC:
|
||
|
tune_time = platform_para_misc.latch_ck_emmc_times;
|
||
|
break;
|
||
|
case MSDC_SD:
|
||
|
tune_time = platform_para_misc.latch_ck_sd_times;
|
||
|
break;
|
||
|
case MSDC_SDIO:
|
||
|
tune_time = platform_para_misc.latch_ck_sdio_times;
|
||
|
break;
|
||
|
default:
|
||
|
tune_time = platform_para_misc.latch_ck_sdio_times;
|
||
|
break;
|
||
|
}
|
||
|
for (j = latch_ck; j < 8; j += (host->hclk / host->sclk)) {
|
||
|
host->tune_latch_ck_cnt = 0;
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_INT_DAT_LATCH_CK_SEL, j);
|
||
|
for (k = 0; k < tune_time; k++) {
|
||
|
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
|
||
|
switch (k) {
|
||
|
case 0:
|
||
|
host->tune_latch_ck_cnt = 1;
|
||
|
break;
|
||
|
default:
|
||
|
host->tune_latch_ck_cnt = k;
|
||
|
break;
|
||
|
}
|
||
|
} else if (opcode == MMC_SEND_TUNING_BLOCK) {
|
||
|
switch (k) {
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 2:
|
||
|
host->tune_latch_ck_cnt = 1;
|
||
|
break;
|
||
|
default:
|
||
|
host->tune_latch_ck_cnt = k - 1;
|
||
|
break;
|
||
|
}
|
||
|
} else if (opcode == MMC_SEND_EXT_CSD) {
|
||
|
host->tune_latch_ck_cnt = k + 1;
|
||
|
} else
|
||
|
host->tune_latch_ck_cnt++;
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_LATCH_CK,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD Fail @ LATCHCK\r\n");
|
||
|
return 0;
|
||
|
} else if ((ret & (E_RES_DAT_CRC
|
||
|
| E_RES_DAT_TMO)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]LATCH_CK %d fail\r\n",
|
||
|
j);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (ret == 0) {
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_INT_DAT_LATCH_CK_SEL, j);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
host->tune_latch_ck_cnt = 0;
|
||
|
|
||
|
return (j >= 8) ? 7 : j;
|
||
|
}
|
||
|
|
||
|
/* online tuning for eMMC4.5(hs200) */
|
||
|
int execute_online_tuning_hs200(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = 0;
|
||
|
int err = 0;
|
||
|
unsigned int response;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
unsigned int uDatEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int score = 0;
|
||
|
unsigned int j, k;
|
||
|
struct AUTOK_REF_INFO *pBdInfo;
|
||
|
char tune_result_str64[65];
|
||
|
u8 p_autok_tune_res[TUNING_PARA_SCAN_COUNT];
|
||
|
unsigned int opcode = MMC_SEND_STATUS;
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
|
||
|
pBdInfo = kmalloc(sizeof(struct AUTOK_REF_INFO), GFP_ATOMIC);
|
||
|
if (!pBdInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
get_platform_func(platform_para_func);
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
|
||
|
autok_init_hs200(host);
|
||
|
memset((void *)p_autok_tune_res, 0,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
|
||
|
/* restore TX value */
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.msdc0_clktx, p_autok_tune_res);
|
||
|
|
||
|
/* Step1 : Tuning Cmd Path */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, CMD_EDGE, &uCmdEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (uCmdEdge)
|
||
|
autok_window_apply(CMD_FALL,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(CMD_RISE,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uCmdEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0) {
|
||
|
host->autok_error = -1;
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(CMD_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
autok_paddly_update(CMD_PAD_RDLY, pBdInfo->opt_dly_cnt,
|
||
|
p_autok_tune_res);
|
||
|
|
||
|
/* Step2 Tuning Data Path (Only Rising Edge Used) */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
/* check device status */
|
||
|
ret = autok_send_tune_cmd(host, 13, TUNE_CMD, &autok_host_para);
|
||
|
if (ret == E_RES_PASS) {
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
AUTOK_RAWPRINT("[AUTOK]dev status 0x%08x\r\n", response);
|
||
|
} else
|
||
|
AUTOK_RAWPRINT("[AUTOK]CMD err while check dev status\r\n");
|
||
|
opcode = MMC_SEND_TUNING_BLOCK_HS200;
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uDatEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, RD_FIFO_EDGE, &uDatEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, DAT_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_DATA, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC
|
||
|
| E_RES_DAT_TMO)) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DAT %d \t %d \t %s\r\n",
|
||
|
uDatEdge, score, tune_result_str64);
|
||
|
if (uDatEdge)
|
||
|
autok_window_apply(DAT_FALL,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(DAT_RISE,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uDatEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0) {
|
||
|
host->autok_error = -1;
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
uDatEdge ^= 0x1;
|
||
|
} while (uDatEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(RD_FIFO_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
autok_paddly_update(DAT_PAD_RDLY, pBdInfo->opt_dly_cnt,
|
||
|
p_autok_tune_res);
|
||
|
autok_param_update(WD_FIFO_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
|
||
|
/* Step3 : Tuning LATCH CK */
|
||
|
if (platform_para_func.new_path_hs200 == 0) {
|
||
|
opcode = MMC_SEND_TUNING_BLOCK_HS200;
|
||
|
p_autok_tune_res[INT_DAT_LATCH_CK] =
|
||
|
autok_tune_latch_ck(host, opcode,
|
||
|
p_autok_tune_res[INT_DAT_LATCH_CK]);
|
||
|
}
|
||
|
|
||
|
autok_result_dump(host, p_autok_tune_res);
|
||
|
|
||
|
#if AUTOK_PARAM_DUMP_ENABLE
|
||
|
autok_register_dump(host);
|
||
|
#endif
|
||
|
msdc_autok_version_apply(p_autok_tune_res);
|
||
|
if (res != NULL) {
|
||
|
memcpy((void *)res, (void *)p_autok_tune_res,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
}
|
||
|
|
||
|
kfree(pBdInfo);
|
||
|
return 0;
|
||
|
fail:
|
||
|
kfree(pBdInfo);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/* online tuning for SDIO3.0 plus */
|
||
|
int execute_online_tuning_sdio30_plus(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
#if (SDIO_PLUS_CMD_TUNE | DS_DLY3_SCAN)
|
||
|
void __iomem *base = host->base;
|
||
|
#endif
|
||
|
unsigned int ret = 0;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int score = 0;
|
||
|
unsigned int j, k;
|
||
|
#if DS_DLY3_SCAN
|
||
|
unsigned int i;
|
||
|
#endif
|
||
|
unsigned int opcode = MMC_SEND_TUNING_BLOCK;
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
int err = 0;
|
||
|
struct AUTOK_REF_INFO *pBdInfo;
|
||
|
#endif
|
||
|
struct AUTOK_REF_INFO_NEW *pInfo;
|
||
|
char tune_result_str64[65];
|
||
|
u8 p_autok_tune_res[TUNING_PARA_SCAN_COUNT];
|
||
|
unsigned int uDatDly = 0;
|
||
|
#if !SDIO_PLUS_CMD_TUNE
|
||
|
u64 RawCmd64 = 0LL;
|
||
|
unsigned int cmd_bd_position = 0;
|
||
|
unsigned int dat_bd_position = 0;
|
||
|
unsigned int cmd_bd_find = 0;
|
||
|
unsigned int dat_bd_find = 0;
|
||
|
#endif
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_PARA_RX platform_para_rx;
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
pBdInfo = kmalloc(sizeof(struct AUTOK_REF_INFO), GFP_ATOMIC);
|
||
|
if (!pBdInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
#endif
|
||
|
pInfo = kmalloc(sizeof(struct AUTOK_REF_INFO_NEW), GFP_ATOMIC);
|
||
|
if (!pInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
#ifdef SDIO_PLUS_CMD_TUNE
|
||
|
kfree(pBdInfo);
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_rx, 0, sizeof(struct AUTOK_PLAT_PARA_RX));
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
get_platform_para_rx(platform_para_rx);
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
|
||
|
autok_init_ddr208(host);
|
||
|
memset((void *)p_autok_tune_res, 0,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
|
||
|
/* restore TX value */
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.sdio30_plus_clktx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_CMD_TX_DLY,
|
||
|
platform_para_tx.sdio30_plus_cmdtx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
platform_para_tx.sdio30_plus_dat0tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
platform_para_tx.sdio30_plus_dat1tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
platform_para_tx.sdio30_plus_dat2tx, p_autok_tune_res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
platform_para_tx.sdio30_plus_dat3tx, p_autok_tune_res);
|
||
|
/* Step1 : Tuning Cmd Path */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
autok_write_param(host, EMMC50_CMD_MUX_EN, 0);
|
||
|
autok_write_param(host, EMMC50_CMD_RESP_LATCH, 0);
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, CMD_EDGE, &uCmdEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if ((ret & E_RES_RSP_CRC) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_CMD_TMO) != 0) {
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (uCmdEdge)
|
||
|
autok_window_apply(CMD_FALL,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(CMD_RISE,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uCmdEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0)
|
||
|
goto fail;
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(CMD_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
autok_paddly_update(CMD_PAD_RDLY, pBdInfo->opt_dly_cnt,
|
||
|
p_autok_tune_res);
|
||
|
|
||
|
/* DLY3 keep default value 20 */
|
||
|
p_autok_tune_res[EMMC50_DS_ZDLY_DLY] = platform_para_rx.ds_dly3_ddr208;
|
||
|
/* Step2 : Tuning DS Clk Path-ZCLK only tune DLY1 */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
/* tune data pad delay , find data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
autok_adjust_paddly(host, &j, DAT_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_DATA, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO | E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0)
|
||
|
break;
|
||
|
else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0) {
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1] = j;
|
||
|
if (j)
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1_SEL] = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
#if DS_DLY3_SCAN
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE,
|
||
|
MSDC_EMMC50_PAD_DS_TUNE_DLY3, i);
|
||
|
#endif
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
RawData64 = 0LL;
|
||
|
/* tune DS delay , base on data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
autok_adjust_paddly(host, &j, DS_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_SDIO_PLUS,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC
|
||
|
| E_RES_DAT_TMO)) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
RawData64 |= 0xffffffff00000000;
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DLY1/2 %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
autok_window_apply(DS_DATA_WIN, RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
#if DS_DLY3_SCAN
|
||
|
}
|
||
|
#endif
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &uDatDly);
|
||
|
autok_paddly_update(DS_PAD_RDLY, uDatDly, p_autok_tune_res);
|
||
|
#else
|
||
|
/* DLY3 keep default value 20 */
|
||
|
p_autok_tune_res[EMMC50_DS_ZDLY_DLY] = platform_para_rx.ds_dly3_ddr208;
|
||
|
/* Step2 : Tuning DS Clk Path-ZCLK only tune DLY1 */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
/* tune data pad delay , find cmd/data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
if (cmd_bd_find == 0)
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
if (dat_bd_find == 0)
|
||
|
autok_adjust_paddly(host, &j, DAT_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_SDIO_PLUS,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO | E_RES_RSP_CRC)) != 0) {
|
||
|
if (cmd_bd_find == 0) {
|
||
|
cmd_bd_find = 1;
|
||
|
cmd_bd_position = j;
|
||
|
}
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0) {
|
||
|
if (dat_bd_find == 0) {
|
||
|
dat_bd_find = 1;
|
||
|
dat_bd_position = j;
|
||
|
}
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
if ((cmd_bd_find == 1) && (dat_bd_find == 1)) {
|
||
|
p_autok_tune_res[CMD_RD_D_DLY1] = cmd_bd_position;
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1] = dat_bd_position;
|
||
|
if (cmd_bd_position)
|
||
|
p_autok_tune_res[CMD_RD_D_DLY1_SEL] = 1;
|
||
|
if (dat_bd_position)
|
||
|
p_autok_tune_res[DAT_RD_D_DLY1_SEL] = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
#if DS_DLY3_SCAN
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE,
|
||
|
MSDC_EMMC50_PAD_DS_TUNE_DLY3, i);
|
||
|
#endif
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
RawData64 = 0LL;
|
||
|
/* tune DS delay , base on cmd/data pad boundary */
|
||
|
for (j = 0; j < 32; j++) {
|
||
|
autok_adjust_paddly(host, &j, DS_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 4; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_SDIO_PLUS,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO | E_RES_RSP_CRC)) != 0)
|
||
|
RawCmd64 |= (u64) (1LL << j);
|
||
|
else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
if ((ret & (E_RES_DAT_CRC | E_RES_DAT_TMO)) != 0)
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
RawCmd64 |= 0xffffffff00000000;
|
||
|
score = autok_simple_score64(tune_result_str64, RawCmd64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DLY1/2 CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
RawData64 |= 0xffffffff00000000;
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DLY1/2 DAT %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
/* check cmd and data DS merge window */
|
||
|
score = autok_simple_score64(tune_result_str64, RawCmd64 | RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]DLY1/2 CMD & DAT MERGE %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (score <= 5)
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][WARN]CMD & DAT MERGE SIZE is %d\r\n",
|
||
|
score);
|
||
|
|
||
|
autok_window_apply(DS_CMD_WIN, RawCmd64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64_new(RawCmd64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
autok_window_apply(DS_DATA_WIN, RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
#if DS_DLY3_SCAN
|
||
|
}
|
||
|
#endif
|
||
|
if (autok_ds_dly_sel(&pInfo->scan_info[0], &uDatDly) == 0) {
|
||
|
autok_paddly_update(DS_PAD_RDLY, uDatDly, p_autok_tune_res);
|
||
|
} else {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
autok_result_dump(host, p_autok_tune_res);
|
||
|
#if AUTOK_PARAM_DUMP_ENABLE
|
||
|
autok_register_dump(host);
|
||
|
#endif
|
||
|
msdc_autok_version_apply(p_autok_tune_res);
|
||
|
if (res != NULL) {
|
||
|
memcpy((void *)res, (void *)p_autok_tune_res,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
}
|
||
|
host->autok_error = 0;
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
kfree(pBdInfo);
|
||
|
#endif
|
||
|
kfree(pInfo);
|
||
|
return 0;
|
||
|
fail:
|
||
|
#if SDIO_PLUS_CMD_TUNE
|
||
|
kfree(pBdInfo);
|
||
|
#endif
|
||
|
kfree(pInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* online tuning for SDIO/SD */
|
||
|
int execute_online_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = 0;
|
||
|
int err = 0;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
unsigned int uDatEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int score = 0;
|
||
|
unsigned int j, k;
|
||
|
unsigned int opcode = MMC_SEND_TUNING_BLOCK;
|
||
|
struct AUTOK_REF_INFO *pBdInfo;
|
||
|
char tune_result_str64[65];
|
||
|
u8 p_autok_tune_res[TUNING_PARA_SCAN_COUNT];
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
|
||
|
pBdInfo = kmalloc(sizeof(struct AUTOK_REF_INFO), GFP_ATOMIC);
|
||
|
if (!pBdInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
get_platform_func(platform_para_func);
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
|
||
|
autok_init_sdr104(host);
|
||
|
memset((void *)p_autok_tune_res, 0,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
|
||
|
/* restore TX value */
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.msdc2_clktx, p_autok_tune_res);
|
||
|
} else if (host->hw->host_function == MSDC_SD) {
|
||
|
if (host->sclk <= 100000000) {
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.msdc1_clktx, p_autok_tune_res);
|
||
|
} else {
|
||
|
autok_param_update(PAD_CLK_TXDLY_AUTOK,
|
||
|
platform_para_tx.msdc1_sdr104_clktx,
|
||
|
p_autok_tune_res);
|
||
|
}
|
||
|
}
|
||
|
/* Step1 : Tuning Cmd Path */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, CMD_EDGE, &uCmdEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, CMD_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host,
|
||
|
opcode, TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
if ((ret & E_RES_RSP_CRC) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_CMD_TMO) != 0) {
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]CMD %d \t %d \t %s\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (uCmdEdge)
|
||
|
autok_window_apply(CMD_FALL, RawData64,
|
||
|
p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(CMD_RISE, RawData64,
|
||
|
p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uCmdEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0) {
|
||
|
host->autok_error = -1;
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(CMD_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
autok_paddly_update(CMD_PAD_RDLY, pBdInfo->opt_dly_cnt,
|
||
|
p_autok_tune_res);
|
||
|
|
||
|
/* Step2 : Tuning Data Path */
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
memset(pBdInfo, 0, sizeof(struct AUTOK_REF_INFO));
|
||
|
|
||
|
uDatEdge = 0;
|
||
|
do {
|
||
|
autok_adjust_param(host, RD_FIFO_EDGE, &uDatEdge, AUTOK_WRITE);
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < 64; j++) {
|
||
|
autok_adjust_paddly(host, &j, DAT_PAD_RDLY);
|
||
|
for (k = 0; k < AUTOK_CMD_TIMES / 2; k++) {
|
||
|
ret = autok_send_tune_cmd(host, opcode,
|
||
|
TUNE_DATA, &autok_host_para);
|
||
|
if ((ret & (E_RES_CMD_TMO
|
||
|
| E_RES_RSP_CRC)) != 0) {
|
||
|
AUTOK_RAWPRINT
|
||
|
("[AUTOK]Err CMD Fail@RD\r\n");
|
||
|
host->autok_error = -1;
|
||
|
goto fail;
|
||
|
} else if ((ret & (E_RES_DAT_CRC
|
||
|
| E_RES_DAT_TMO)) != 0) {
|
||
|
RawData64 |= (u64) (1LL << j);
|
||
|
break;
|
||
|
} else if ((ret & E_RES_FATAL_ERR) != 0)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DAT %d \t %d \t %s\r\n",
|
||
|
uDatEdge, score, tune_result_str64);
|
||
|
if (uDatEdge)
|
||
|
autok_window_apply(DAT_FALL,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
else
|
||
|
autok_window_apply(DAT_RISE,
|
||
|
RawData64, p_autok_tune_res);
|
||
|
if (autok_check_scan_res64(RawData64,
|
||
|
&pBdInfo->scan_info[uDatEdge],
|
||
|
AUTOK_TUNING_INACCURACY) != 0) {
|
||
|
host->autok_error = -1;
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
uDatEdge ^= 0x1;
|
||
|
} while (uDatEdge);
|
||
|
|
||
|
err = autok_pad_dly_sel(pBdInfo);
|
||
|
if (err == -2) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK][Error]======Analysis Failed!!======\r\n");
|
||
|
goto fail;
|
||
|
}
|
||
|
autok_param_update(RD_FIFO_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
autok_paddly_update(DAT_PAD_RDLY, pBdInfo->opt_dly_cnt,
|
||
|
p_autok_tune_res);
|
||
|
autok_param_update(WD_FIFO_EDGE, pBdInfo->opt_edge_sel,
|
||
|
p_autok_tune_res);
|
||
|
|
||
|
autok_tuning_parameter_init(host, p_autok_tune_res);
|
||
|
|
||
|
/* Step3 : Tuning LATCH CK */
|
||
|
if (platform_para_func.new_path_sdr104 == 0) {
|
||
|
opcode = MMC_SEND_TUNING_BLOCK;
|
||
|
p_autok_tune_res[INT_DAT_LATCH_CK] =
|
||
|
autok_tune_latch_ck(host, opcode,
|
||
|
p_autok_tune_res[INT_DAT_LATCH_CK]);
|
||
|
}
|
||
|
|
||
|
autok_result_dump(host, p_autok_tune_res);
|
||
|
#if AUTOK_PARAM_DUMP_ENABLE
|
||
|
autok_register_dump(host);
|
||
|
#endif
|
||
|
msdc_autok_version_apply(p_autok_tune_res);
|
||
|
if (res != NULL) {
|
||
|
memcpy((void *)res, (void *)p_autok_tune_res,
|
||
|
sizeof(p_autok_tune_res) / sizeof(u8));
|
||
|
}
|
||
|
host->autok_error = 0;
|
||
|
|
||
|
kfree(pBdInfo);
|
||
|
return 0;
|
||
|
fail:
|
||
|
kfree(pBdInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void autok_msdc_tx_setting(struct msdc_host *host, struct mmc_ios *ios)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
struct AUTOK_PLAT_PARA_TX platform_para_tx;
|
||
|
unsigned int value;
|
||
|
unsigned int clk_mode;
|
||
|
|
||
|
memset(&platform_para_tx, 0, sizeof(struct AUTOK_PLAT_PARA_TX));
|
||
|
get_platform_para_tx(platform_para_tx);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKMOD, clk_mode);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
if (ios->timing == MMC_TIMING_MMC_HS400) {
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0,
|
||
|
MSDC_EMMC50_CFG_TXSKEW_SEL,
|
||
|
platform_para_tx.msdc0_hs400_txskew);
|
||
|
value = platform_para_tx.msdc0_hs400_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_cmdtx;
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat0tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat1tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat2tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat3tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat4tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat5tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat6tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_hs400_dat7tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
} else if (ios->timing == MMC_TIMING_MMC_HS200) {
|
||
|
MSDC_SET_FIELD(EMMC50_CFG0,
|
||
|
MSDC_EMMC50_CFG_TXSKEW_SEL,
|
||
|
platform_para_tx.msdc0_hs400_txskew);
|
||
|
} else {
|
||
|
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_DDR50CKD,
|
||
|
platform_para_tx.msdc0_ddr_ckd);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_DDR50CKD, 0);
|
||
|
}
|
||
|
value = platform_para_tx.msdc0_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_cmdtx;
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat0tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat1tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat2tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat3tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat4tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat5tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat6tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.msdc0_dat7tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
}
|
||
|
} else if (host->hw->host_function == MSDC_SD) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_DDR50CKD, platform_para_tx.msdc1_ddr_ckd);
|
||
|
if (ios->timing == MMC_TIMING_UHS_SDR104) {
|
||
|
value = platform_para_tx.msdc1_sdr104_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
} else {
|
||
|
value = platform_para_tx.msdc1_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
}
|
||
|
} else if (host->hw->host_function == MSDC_SDIO) {
|
||
|
if ((ios->timing == MMC_TIMING_UHS_DDR50)
|
||
|
&& (host->sclk > 200000000)) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_DDR50CKD,
|
||
|
platform_para_tx.msdc2_ddr_ckd);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_DDR50CKD, 0);
|
||
|
}
|
||
|
if (clk_mode == 3) {
|
||
|
value = platform_para_tx.sdio30_plus_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.sdio30_plus_cmdtx;
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.sdio30_plus_dat0tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.sdio30_plus_dat1tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.sdio30_plus_dat2tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
value = platform_para_tx.sdio30_plus_dat3tx;
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&value, AUTOK_WRITE);
|
||
|
} else {
|
||
|
value = platform_para_tx.msdc2_clktx;
|
||
|
autok_adjust_param(host, PAD_CLK_TXDLY_AUTOK,
|
||
|
&value, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_msdc_tx_setting);
|
||
|
|
||
|
void autok_low_speed_switch_edge(struct msdc_host *host,
|
||
|
struct mmc_ios *ios, enum ERROR_TYPE error_type)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int orig_resp_edge, orig_crc_fifo_edge;
|
||
|
unsigned int cur_resp_edge, cur_crc_fifo_edge;
|
||
|
unsigned int orig_read_edge, orig_read_fifo_edge;
|
||
|
unsigned int cur_read_edge, cur_read_fifo_edge;
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK][low speed switch edge]======start======\r\n");
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
switch (error_type) {
|
||
|
case CMD_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
cur_resp_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][CMD err]edge %d->%d\r\n"
|
||
|
, orig_resp_edge, cur_resp_edge);
|
||
|
break;
|
||
|
case DATA_ERROR:
|
||
|
#ifdef PORT0_PB0_RD_DAT_SEL_VALID
|
||
|
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge ^ 0x1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]fifo_edge = %d",
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("edge %d->%d\r\n",
|
||
|
orig_read_edge, cur_read_edge);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge = %d",
|
||
|
cur_read_edge);
|
||
|
AUTOK_RAWPRINT("fifo_edge %d->%d\r\n",
|
||
|
orig_read_fifo_edge,
|
||
|
cur_read_fifo_edge);
|
||
|
}
|
||
|
#else
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL, orig_read_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL, cur_read_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge %d->%d\r\n",
|
||
|
orig_read_edge, cur_read_edge);
|
||
|
#endif
|
||
|
break;
|
||
|
case CRC_STATUS_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
cur_crc_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][WR err]edge %d->%d\r\n",
|
||
|
orig_crc_fifo_edge, cur_crc_fifo_edge);
|
||
|
break;
|
||
|
}
|
||
|
} else if (host->hw->host_function == MSDC_SD) {
|
||
|
switch (error_type) {
|
||
|
case CMD_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
cur_resp_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][CMD err]edge %d->%d\r\n",
|
||
|
orig_resp_edge, cur_resp_edge);
|
||
|
break;
|
||
|
case DATA_ERROR:
|
||
|
#ifdef PORT1_PB0_RD_DAT_SEL_VALID
|
||
|
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge ^ 0x1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]fifo_edge = %d",
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("edge %d->%d\r\n",
|
||
|
orig_read_edge, cur_read_edge);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge = %d",
|
||
|
cur_read_edge);
|
||
|
AUTOK_RAWPRINT("fifo_edge %d->%d\r\n",
|
||
|
orig_read_fifo_edge, cur_read_fifo_edge);
|
||
|
}
|
||
|
#else
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge %d->%d\r\n"
|
||
|
, orig_read_edge, cur_read_edge);
|
||
|
#endif
|
||
|
break;
|
||
|
case CRC_STATUS_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
cur_crc_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][WR err]edge %d->%d\r\n"
|
||
|
, orig_crc_fifo_edge, cur_crc_fifo_edge);
|
||
|
break;
|
||
|
}
|
||
|
} else if (host->hw->host_function == MSDC_SDIO) {
|
||
|
switch (error_type) {
|
||
|
case CMD_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
orig_resp_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_RSPL,
|
||
|
cur_resp_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][CMD err]edge %d->%d\r\n",
|
||
|
orig_resp_edge, cur_resp_edge);
|
||
|
break;
|
||
|
case DATA_ERROR:
|
||
|
#ifdef PORT3_PB0_RD_DAT_SEL_VALID
|
||
|
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge ^ 0x1);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]fifo_edge = %d",
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("edge %d->%d\r\n",
|
||
|
orig_read_edge, cur_read_edge);
|
||
|
} else {
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
orig_read_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL,
|
||
|
cur_read_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge = %d",
|
||
|
cur_read_edge);
|
||
|
AUTOK_RAWPRINT("fifo_edge %d->%d\r\n",
|
||
|
orig_read_fifo_edge, cur_read_fifo_edge);
|
||
|
}
|
||
|
#else
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL_SEL, 0);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT0,
|
||
|
MSDC_PB0_RD_DAT_SEL, 0);
|
||
|
MSDC_SET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
orig_read_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_IOCON,
|
||
|
MSDC_IOCON_R_D_SMPL,
|
||
|
cur_read_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][RD err]edge %d->%d\r\n",
|
||
|
orig_read_edge, cur_read_edge);
|
||
|
#endif
|
||
|
break;
|
||
|
case CRC_STATUS_ERROR:
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge);
|
||
|
MSDC_SET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
orig_crc_fifo_edge ^ 0x1);
|
||
|
MSDC_GET_FIELD(MSDC_PATCH_BIT2,
|
||
|
MSDC_PB2_CFGCRCSTSEDGE,
|
||
|
cur_crc_fifo_edge);
|
||
|
AUTOK_RAWPRINT("[AUTOK][WR err]edge %d->%d\r\n",
|
||
|
orig_crc_fifo_edge, cur_crc_fifo_edge);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
AUTOK_RAWPRINT("[AUTOK][low speed switch edge]======end======\r\n");
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_low_speed_switch_edge);
|
||
|
|
||
|
void autok_msdc_device_rx_set(struct msdc_host *host,
|
||
|
unsigned int cmd_tx, unsigned int data_p_tx, unsigned int data_n_tx)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = E_RES_PASS;
|
||
|
unsigned int base_addr = 0;
|
||
|
unsigned int func_num = 0;
|
||
|
unsigned int reg_value = 0;
|
||
|
unsigned int r_w_dirc = 0;
|
||
|
unsigned int i;
|
||
|
|
||
|
base_addr = 0x11c;
|
||
|
func_num = 0x1;
|
||
|
if (cmd_tx == 0)
|
||
|
reg_value = 0;
|
||
|
else
|
||
|
reg_value = (1 << 7) + cmd_tx;
|
||
|
r_w_dirc = EXT_WRITE;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x124 + i;
|
||
|
func_num = 0x1;
|
||
|
if (data_p_tx == 0)
|
||
|
reg_value = 0;
|
||
|
else
|
||
|
reg_value = data_p_tx + (1 << 7);
|
||
|
r_w_dirc = EXT_WRITE;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x128 + i;
|
||
|
func_num = 0x1;
|
||
|
if (data_n_tx == 0)
|
||
|
reg_value = 0;
|
||
|
else
|
||
|
reg_value = data_n_tx + (1 << 7);
|
||
|
r_w_dirc = EXT_WRITE;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
}
|
||
|
/* read back setting check */
|
||
|
base_addr = 0x11c;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
&cmd_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
cmd_tx = MSDC_READ32(SDC_RESP0) & 0xFF;
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x read fail\r\n",
|
||
|
base_addr);
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x124 + i;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
&data_p_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
else
|
||
|
data_p_tx = (data_p_tx << (i * 8))
|
||
|
| (MSDC_READ32(SDC_RESP0) & 0xFF);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x128 + i;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
&data_n_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
else
|
||
|
data_n_tx = (data_n_tx << (i * 8))
|
||
|
| (MSDC_READ32(SDC_RESP0) & 0xFF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void autok_msdc_device_rx_get(struct msdc_host *host, unsigned int *cmd_tx,
|
||
|
unsigned int *data_p_tx, unsigned int *data_n_tx)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = E_RES_PASS;
|
||
|
unsigned int base_addr = 0;
|
||
|
unsigned int func_num = 0;
|
||
|
unsigned int r_w_dirc = 0;
|
||
|
unsigned int i;
|
||
|
|
||
|
/* read back setting check */
|
||
|
base_addr = 0x11c;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
cmd_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x read fail\r\n",
|
||
|
base_addr);
|
||
|
else
|
||
|
*cmd_tx = MSDC_READ32(SDC_RESP0) & 0xFF;
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x124 + i;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
data_p_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
else
|
||
|
*data_p_tx = (*data_p_tx << (i * 8))
|
||
|
| (MSDC_READ32(SDC_RESP0) & 0xFF);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
base_addr = 0x128 + i;
|
||
|
func_num = 0x1;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
data_n_tx, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DRS reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
else
|
||
|
*data_n_tx = (*data_n_tx << (i * 8))
|
||
|
| (MSDC_READ32(SDC_RESP0) & 0xFF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int autok_offline_tuning_device_RX(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
#if AUTOK_OFFLINE_DAT_D_RX_ENABLE
|
||
|
unsigned int dat_rx_sel = 0;
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_CMD_D_RX_ENABLE
|
||
|
unsigned int cmd_rx_sel = 0;
|
||
|
#endif
|
||
|
|
||
|
#if (AUTOK_OFFLINE_CMD_D_RX_ENABLE || AUTOK_OFFLINE_DAT_D_RX_ENABLE)
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int tune_rx_value;
|
||
|
unsigned char tune_cnt;
|
||
|
unsigned char i;
|
||
|
unsigned char tune_crc_cnt[32];
|
||
|
unsigned char tune_pass_cnt[32];
|
||
|
unsigned char tune_tmo_cnt[32];
|
||
|
char tune_result[33];
|
||
|
unsigned int cmd_tx = 0;
|
||
|
unsigned int dat_tx[4] = {0};
|
||
|
unsigned int cmd_init_tx;
|
||
|
unsigned int dat_init_tx[4] = {0};
|
||
|
|
||
|
unsigned int check_cnt = 0;
|
||
|
unsigned int iorx = 0;
|
||
|
unsigned int base_addr = 0;
|
||
|
unsigned int func_num = 0;
|
||
|
unsigned int reg_value = 0;
|
||
|
unsigned int r_w_dirc = 0;
|
||
|
unsigned int cmd_rx = 0;
|
||
|
unsigned int data_p_rx = 0;
|
||
|
unsigned int data_n_rx = 0;
|
||
|
u64 Rx64 = 0LL;
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_REF_INFO_NEW *pInfo;
|
||
|
|
||
|
pInfo = kmalloc(sizeof(struct AUTOK_REF_INFO_NEW), GFP_ATOMIC);
|
||
|
if (!pInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
Rx64 = 0LL;
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK]SDIO device function enable\r\n");
|
||
|
/* read previous device setting */
|
||
|
base_addr = 0x02;
|
||
|
func_num = 0x0;
|
||
|
reg_value = 0;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (reg_value & 0x02)
|
||
|
goto tune_device_rx;
|
||
|
/* function has not enabled, enable device function1 */
|
||
|
base_addr = 0x02;
|
||
|
func_num = 0x0;
|
||
|
reg_value |= 0x02;
|
||
|
r_w_dirc = EXT_WRITE;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]IOEx reg 0x%x set fail\r\n", base_addr);
|
||
|
AUTOK_RAWPRINT("[AUTOK]SDIO device function enable ready check\r\n");
|
||
|
while ((!(iorx & 0x02)) && (check_cnt < 10)) {
|
||
|
check_cnt++;
|
||
|
base_addr = 0x03;
|
||
|
func_num = 0x0;
|
||
|
reg_value = 0x00;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]IOEx reg 0x%x set fail\r\n",
|
||
|
base_addr);
|
||
|
iorx = MSDC_READ32(SDC_RESP0) & 0xff;
|
||
|
AUTOK_RAWPRINT("[AUTOK]iorx 0x%x\r\n", iorx);
|
||
|
}
|
||
|
tune_device_rx:
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx, AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_READ);
|
||
|
cmd_init_tx = 0;
|
||
|
dat_init_tx[0] = 0;
|
||
|
dat_init_tx[1] = 0;
|
||
|
dat_init_tx[2] = 0;
|
||
|
dat_init_tx[3] = 0;
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_init_tx, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_init_tx[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_init_tx[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_init_tx[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_init_tx[3], AUTOK_WRITE);
|
||
|
/* store tx setting */
|
||
|
autok_msdc_device_rx_get(host, &cmd_rx, &data_p_rx, &data_n_rx);
|
||
|
AUTOK_RAWPRINT("[AUTOK]pre SDIO cmd rx %x data rx = %x %x\r\n",
|
||
|
cmd_rx, data_p_rx, data_n_rx);
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_CMD_D_RX_ENABLE
|
||
|
/* Tuning Cmd TX */
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune device cmd RX]======start======\r\n");
|
||
|
/* Step1 : Tuning Cmd TX */
|
||
|
for (tune_rx_value = 0; tune_rx_value < 32; tune_rx_value++) {
|
||
|
tune_tmo_cnt[tune_rx_value] = 0;
|
||
|
tune_crc_cnt[tune_rx_value] = 0;
|
||
|
tune_pass_cnt[tune_rx_value] = 0;
|
||
|
autok_msdc_device_rx_set(host, tune_rx_value,
|
||
|
data_p_rx & 0x1f, data_n_rx & 0x1f);
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT; tune_cnt++) {
|
||
|
ret = autok_send_tune_cmd(host, MMC_SEND_TUNING_BLOCK,
|
||
|
TUNE_SDIO_PLUS, &autok_host_para);
|
||
|
if ((ret & E_RES_CMD_TMO) != 0) {
|
||
|
tune_tmo_cnt[tune_rx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_rx_value);
|
||
|
} else if ((ret&(E_RES_RSP_CRC)) != 0) {
|
||
|
tune_crc_cnt[tune_rx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_rx_value);
|
||
|
} else if ((ret & (E_RES_PASS)) == 0)
|
||
|
tune_pass_cnt[tune_rx_value]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if ((tune_tmo_cnt[i] != 0) || (tune_crc_cnt[i] != 0))
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_pass_cnt[i] == TUNE_TX_CNT)
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune_dev_cmd_RX 0-31 %s\r\n", tune_result);
|
||
|
/* select a best cmd rx setting, default setting may can not work */
|
||
|
Rx64 |= 0xffffffff00000000;
|
||
|
autok_check_scan_res64_new(Rx64, &pInfo->scan_info[0], 0);
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &cmd_rx_sel);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune dev cmd RX sel:%d\r\n", cmd_rx_sel);
|
||
|
autok_msdc_device_rx_set(host, cmd_rx_sel, 0, 0);
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY, &cmd_tx, AUTOK_WRITE);
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune dev cmd RX]======end======\r\n");
|
||
|
if (res != NULL)
|
||
|
autok_window_apply(D_CMD_RX, Rx64, res);
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_DAT_D_RX_ENABLE
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune dev data RX]======start======\r\n");
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
Rx64 = 0LL;
|
||
|
/* Tuning Data TX */
|
||
|
for (tune_rx_value = 0; tune_rx_value < 32; tune_rx_value++) {
|
||
|
tune_tmo_cnt[tune_rx_value] = 0;
|
||
|
tune_crc_cnt[tune_rx_value] = 0;
|
||
|
tune_pass_cnt[tune_rx_value] = 0;
|
||
|
|
||
|
autok_msdc_device_rx_set(host, cmd_rx & 0x1f,
|
||
|
tune_rx_value, tune_rx_value);
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT; tune_cnt++) {
|
||
|
/* send cmd53 write data */
|
||
|
ret = autok_send_tune_cmd(host,
|
||
|
SD_IO_RW_EXTENDED, TUNE_SDIO_PLUS,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_RSP_CRC | E_RES_CMD_TMO)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune dat RX cmd%d err\n",
|
||
|
MMC_WRITE_BLOCK);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune dat RX fail\n");
|
||
|
goto end;
|
||
|
}
|
||
|
if ((ret & E_RES_DAT_TMO) != 0) {
|
||
|
tune_tmo_cnt[tune_rx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_rx_value);
|
||
|
/* send CMD52 abort command */
|
||
|
autok_send_tune_cmd(host,
|
||
|
SD_IO_RW_DIRECT,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
} else if ((ret & (E_RES_DAT_CRC)) != 0) {
|
||
|
tune_crc_cnt[tune_rx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_rx_value);
|
||
|
/* send CMD52 abort command */
|
||
|
autok_send_tune_cmd(host,
|
||
|
SD_IO_RW_DIRECT,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
} else if ((ret & (E_RES_PASS)) == 0)
|
||
|
tune_pass_cnt[tune_rx_value]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if ((tune_tmo_cnt[i] != 0) || (tune_crc_cnt[i] != 0))
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_pass_cnt[i] == TUNE_TX_CNT)
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
AUTOK_RAWPRINT("[AUTOK]device DAT RX 0 - 31 %s\r\n", tune_result);
|
||
|
/* select a best dat rx setting */
|
||
|
Rx64 |= 0xffffffff00000000;
|
||
|
autok_check_scan_res64_new(Rx64, &pInfo->scan_info[0], 0);
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &dat_rx_sel);
|
||
|
AUTOK_RAWPRINT("[AUTOK]device DAT RX dly:%d\r\n", dat_rx_sel);
|
||
|
/* restore data rx setting */
|
||
|
autok_msdc_device_rx_set(host, cmd_rx & 0x1f,
|
||
|
data_p_rx & 0x1f, data_n_rx & 0x1f);
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_WRITE);
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune device data RX]======end======\r\n");
|
||
|
if (res != NULL)
|
||
|
autok_window_apply(D_DATA_RX, Rx64, res);
|
||
|
end:
|
||
|
#endif
|
||
|
kfree(pInfo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int autok_offline_tuning_clk_TX(struct msdc_host *host, unsigned int opcode)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int response;
|
||
|
unsigned int data_pin_status = 0xff;
|
||
|
unsigned int tune_tx_value;
|
||
|
unsigned char tune_cnt;
|
||
|
unsigned char i;
|
||
|
unsigned char tune_crc_cnt[32];
|
||
|
unsigned char tune_pass_cnt[32];
|
||
|
unsigned char tune_tmo_cnt[32];
|
||
|
char tune_result[33];
|
||
|
unsigned int clk_tx;
|
||
|
struct autok_host autok_host_para;
|
||
|
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune clk TX]=========start========\r\n");
|
||
|
/* store tx setting */
|
||
|
MSDC_GET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CLKTXDLY, clk_tx);
|
||
|
|
||
|
/* Step1 : Tuning Clk TX */
|
||
|
for (tune_tx_value = 0; tune_tx_value < 32; tune_tx_value++) {
|
||
|
tune_tmo_cnt[tune_tx_value] = 0;
|
||
|
tune_crc_cnt[tune_tx_value] = 0;
|
||
|
tune_pass_cnt[tune_tx_value] = 0;
|
||
|
MSDC_SET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CLKTXDLY,
|
||
|
tune_tx_value);
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT; tune_cnt++) {
|
||
|
ret = autok_send_tune_cmd(host,
|
||
|
MMC_SEND_TUNING_BLOCK,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
if ((ret & E_RES_CMD_TMO) != 0) {
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
tune_tmo_cnt[tune_tx_value]++;
|
||
|
} else if ((ret&(E_RES_RSP_CRC)) != 0)
|
||
|
tune_crc_cnt[tune_tx_value]++;
|
||
|
else if ((ret & (E_RES_PASS)) == 0)
|
||
|
tune_pass_cnt[tune_tx_value]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if (tune_tmo_cnt[i] != 0)
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_crc_cnt[i] != 0)
|
||
|
tune_result[i] = 'R';
|
||
|
else if (tune_pass_cnt[i] == TUNE_TX_CNT)
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune_clk_TX 0 - 31 %s\r\n", tune_result);
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune clk TX]=========end========\r\n");
|
||
|
|
||
|
/* restore clk tx setting */
|
||
|
MSDC_SET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CLKTXDLY, clk_tx);
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune clk TX @write data]======start======\r\n");
|
||
|
|
||
|
/* Step2 : Tuning clk TX @ write data */
|
||
|
for (tune_tx_value = 0; tune_tx_value < 32; tune_tx_value++) {
|
||
|
tune_tmo_cnt[tune_tx_value] = 0;
|
||
|
tune_crc_cnt[tune_tx_value] = 0;
|
||
|
tune_pass_cnt[tune_tx_value] = 0;
|
||
|
autok_host_para.clk_tx = tune_tx_value;
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT / 2; tune_cnt++) {
|
||
|
/* check device status */
|
||
|
if (opcode == MMC_WRITE_BLOCK) {
|
||
|
MSDC_GET_FIELD(MSDC_PS,
|
||
|
MSDC_PS_DAT,
|
||
|
data_pin_status);
|
||
|
if (!(data_pin_status & 0x1)) {
|
||
|
autok_send_tune_cmd(host,
|
||
|
MMC_STOP_TRANSMISSION,
|
||
|
TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
while (!(data_pin_status & 0x1))
|
||
|
MSDC_GET_FIELD(MSDC_PS,
|
||
|
MSDC_PS_DAT,
|
||
|
data_pin_status);
|
||
|
}
|
||
|
}
|
||
|
/* send cmd24 write one block data */
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_DATA,
|
||
|
&autok_host_para);
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
if ((ret & (E_RES_RSP_CRC | E_RES_CMD_TMO)) != 0) {
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune clk TX cmd%d err\n",
|
||
|
MMC_WRITE_BLOCK);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune clk TX fail\n");
|
||
|
goto end;
|
||
|
}
|
||
|
if ((ret & E_RES_DAT_TMO) != 0)
|
||
|
tune_tmo_cnt[tune_tx_value]++;
|
||
|
else if ((ret & (E_RES_DAT_CRC)) != 0)
|
||
|
tune_crc_cnt[tune_tx_value]++;
|
||
|
else if ((ret & (E_RES_PASS)) == 0)
|
||
|
tune_pass_cnt[tune_tx_value]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if ((tune_tmo_cnt[i] != 0) || (tune_crc_cnt[i] != 0))
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_pass_cnt[i] == (TUNE_TX_CNT / 2))
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune_clk_TX 0 - 31 %s\r\n", tune_result);
|
||
|
|
||
|
end:
|
||
|
/* restore clk tx setting */
|
||
|
MSDC_SET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CLKTXDLY, clk_tx);
|
||
|
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune clk TX @write data]======end======\r\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int autok_emmc_tune_tx(struct msdc_host *host, unsigned int opcode)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int response;
|
||
|
struct autok_host autok_host_para;
|
||
|
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
/* check device status */
|
||
|
response = 0;
|
||
|
while (((response >> 9) & 0xF) != 4) {
|
||
|
ret = autok_send_tune_cmd(host, MMC_SEND_STATUS, TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_RSP_CRC | E_RES_CMD_TMO)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX cmd13 err\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
if ((((response >> 9) & 0xF) == 5)
|
||
|
|| (((response >> 9) & 0xF) == 6))
|
||
|
ret = autok_send_tune_cmd(host,
|
||
|
MMC_STOP_TRANSMISSION, TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
}
|
||
|
|
||
|
/* send cmd24/cmd23-cmd25 write one block data */
|
||
|
if (opcode == MMC_WRITE_MULTIPLE_BLOCK) {
|
||
|
ret = autok_send_tune_cmd(host, MMC_SET_BLOCK_COUNT, TUNE_CMD,
|
||
|
&autok_host_para);
|
||
|
if ((ret & (E_RES_RSP_CRC | E_RES_CMD_TMO)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX cmd23 err\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int autok_tune_data_tx(struct msdc_host *host,
|
||
|
struct autok_host *host_para,
|
||
|
unsigned int opcode,
|
||
|
unsigned int tx_value, u64 *rx64,
|
||
|
unsigned char *crc_cnt, unsigned char *tmo_cnt,
|
||
|
unsigned char *pass_cnt)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int response;
|
||
|
unsigned char tune_cnt;
|
||
|
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT; tune_cnt++) {
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
if (autok_emmc_tune_tx(host, opcode) == -1)
|
||
|
return -1;
|
||
|
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_DATA,
|
||
|
host_para);
|
||
|
response = MSDC_READ32(SDC_RESP0);
|
||
|
} else {
|
||
|
/* send cmd53 write data */
|
||
|
opcode = SD_IO_RW_EXTENDED;
|
||
|
ret = autok_send_tune_cmd(host, opcode, TUNE_SDIO_PLUS,
|
||
|
host_para);
|
||
|
}
|
||
|
if ((ret & (E_RES_RSP_CRC | E_RES_CMD_TMO)) != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX cmd%d err\n",
|
||
|
opcode);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune data TX fail\n");
|
||
|
return -1;
|
||
|
}
|
||
|
if ((ret & E_RES_DAT_TMO) != 0) {
|
||
|
(*tmo_cnt)++;
|
||
|
*rx64 |= (u64) (1LL << tx_value);
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& ((opcode == MMC_WRITE_MULTIPLE_BLOCK)
|
||
|
|| (opcode == MMC_EXECUTE_WRITE_TASK))) {
|
||
|
autok_send_tune_cmd(host,
|
||
|
MMC_STOP_TRANSMISSION,
|
||
|
TUNE_CMD, host_para);
|
||
|
}
|
||
|
/* send CMD52 abort command */
|
||
|
if (host->hw->host_function == MSDC_SDIO)
|
||
|
autok_send_tune_cmd(host,
|
||
|
SD_IO_RW_DIRECT,
|
||
|
TUNE_CMD, host_para);
|
||
|
} else if ((ret & (E_RES_DAT_CRC)) != 0) {
|
||
|
(*crc_cnt)++;
|
||
|
*rx64 |= (u64) (1LL << tx_value);
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& ((opcode == MMC_WRITE_MULTIPLE_BLOCK)
|
||
|
|| (opcode == MMC_EXECUTE_WRITE_TASK))) {
|
||
|
autok_send_tune_cmd(host,
|
||
|
MMC_STOP_TRANSMISSION,
|
||
|
TUNE_CMD, host_para);
|
||
|
}
|
||
|
/* send CMD52 abort command */
|
||
|
if (host->hw->host_function == MSDC_SDIO)
|
||
|
autok_send_tune_cmd(host,
|
||
|
SD_IO_RW_DIRECT,
|
||
|
TUNE_CMD, host_para);
|
||
|
} else if ((ret & (E_RES_PASS)) == 0)
|
||
|
(*pass_cnt)++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int autok_offline_tuning_TX(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
#if (AUTOK_OFFLINE_CMD_H_TX_ENABLE || AUTOK_OFFLINE_DAT_H_TX_ENABLE)
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int tune_tx_value;
|
||
|
unsigned char i;
|
||
|
unsigned char tune_crc_cnt[32];
|
||
|
unsigned char tune_pass_cnt[32];
|
||
|
unsigned char tune_tmo_cnt[32];
|
||
|
char tune_result[33];
|
||
|
unsigned int cmd_tx;
|
||
|
unsigned int dat_tx[8] = {0};
|
||
|
u64 Rx64 = 0LL;
|
||
|
|
||
|
unsigned int check_cnt = 0;
|
||
|
unsigned int iorx = 0;
|
||
|
unsigned int base_addr = 0;
|
||
|
unsigned int func_num = 0;
|
||
|
unsigned int reg_value = 0;
|
||
|
unsigned int r_w_dirc = 0;
|
||
|
unsigned int cmd_rx = 0;
|
||
|
unsigned int data_p_rx = 0;
|
||
|
unsigned int data_n_rx = 0;
|
||
|
struct AUTOK_REF_INFO_NEW *pInfo;
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_CMD_H_TX_ENABLE
|
||
|
unsigned char tune_cnt;
|
||
|
unsigned int cmd_tx_sel = 0;
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_DAT_H_TX_ENABLE
|
||
|
unsigned int dat_tx_sel = 0;
|
||
|
unsigned int dat_tx_separ_sel[8] = {0};
|
||
|
unsigned int separate_tune_start = 0;
|
||
|
unsigned int separate_tune_cnt = 1;
|
||
|
unsigned int j;
|
||
|
unsigned int opcode = MMC_WRITE_BLOCK;
|
||
|
u64 Rx64_separate[8] = {0LL};
|
||
|
unsigned char crc_cnt, tmo_cnt, pass_cnt;
|
||
|
struct autok_host autok_host_para;
|
||
|
struct AUTOK_PLAT_PARA_MISC platform_para_misc;
|
||
|
|
||
|
pInfo = kmalloc(sizeof(struct AUTOK_REF_INFO_NEW), GFP_ATOMIC);
|
||
|
if (!pInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
memset(&autok_host_para, 0, sizeof(struct autok_host));
|
||
|
memset(&platform_para_misc, 0, sizeof(struct AUTOK_PLAT_PARA_MISC));
|
||
|
get_platform_para_misc(platform_para_misc);
|
||
|
#endif
|
||
|
|
||
|
#if (AUTOK_OFFLINE_CMD_H_TX_ENABLE || AUTOK_OFFLINE_DAT_H_TX_ENABLE)
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
/* read previous device setting */
|
||
|
base_addr = 0x02;
|
||
|
func_num = 0x0;
|
||
|
reg_value = 0;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (reg_value & 0x02)
|
||
|
goto tune_host_tx;
|
||
|
/* function has not enabled, enable device function1 */
|
||
|
AUTOK_RAWPRINT("[AUTOK]SDIO dev func enable\r\n");
|
||
|
base_addr = 0x02;
|
||
|
func_num = 0x0;
|
||
|
reg_value |= 0x02;
|
||
|
r_w_dirc = EXT_WRITE;
|
||
|
ret = autok_sdio_device_rx_set(host, func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]set reg 0x%x fail\r\n",
|
||
|
base_addr);
|
||
|
AUTOK_RAWPRINT("[AUTOK]dev func enable ready check\r\n");
|
||
|
while ((!(iorx & 0x02)) && (check_cnt < 10)) {
|
||
|
check_cnt++;
|
||
|
base_addr = 0x03;
|
||
|
func_num = 0x0;
|
||
|
reg_value = 0x00;
|
||
|
r_w_dirc = EXT_READ;
|
||
|
ret = autok_sdio_device_rx_set(host,
|
||
|
func_num, base_addr,
|
||
|
®_value, r_w_dirc, SD_IO_RW_DIRECT);
|
||
|
if (ret != E_RES_PASS)
|
||
|
AUTOK_RAWPRINT("[AUTOK]set reg 0x%x fail\r\n",
|
||
|
base_addr);
|
||
|
iorx = MSDC_READ32(SDC_RESP0) & 0xff;
|
||
|
AUTOK_RAWPRINT("[AUTOK]iorx 0x%x\r\n", iorx);
|
||
|
}
|
||
|
tune_host_tx:
|
||
|
/* store tx setting */
|
||
|
autok_msdc_device_rx_get(host, &cmd_rx,
|
||
|
&data_p_rx, &data_n_rx);
|
||
|
}
|
||
|
/* store tx setting */
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx, AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&dat_tx[4], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&dat_tx[5], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&dat_tx[6], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&dat_tx[7], AUTOK_READ);
|
||
|
} else {
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx, AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_READ);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_READ);
|
||
|
}
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_CMD_H_TX_ENABLE
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune cmd TX]======start======\r\n");
|
||
|
autok_msdc_device_rx_set(host, 0, 0, 0);
|
||
|
AUTOK_RAWPRINT("[AUTOK][device RX set]CMD:0 D0-3:0 D4-7:0\r\n");
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
/* Step1 : Tuning Cmd TX */
|
||
|
for (tune_tx_value = 0; tune_tx_value < 32; tune_tx_value++) {
|
||
|
tune_tmo_cnt[tune_tx_value] = 0;
|
||
|
tune_crc_cnt[tune_tx_value] = 0;
|
||
|
tune_pass_cnt[tune_tx_value] = 0;
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
for (tune_cnt = 0; tune_cnt < TUNE_TX_CNT; tune_cnt++) {
|
||
|
if (host->hw->host_function == MSDC_EMMC)
|
||
|
ret = autok_send_tune_cmd(host, MMC_SEND_STATUS,
|
||
|
TUNE_CMD, &autok_host_para);
|
||
|
else
|
||
|
ret = autok_send_tune_cmd(host,
|
||
|
MMC_SEND_TUNING_BLOCK,
|
||
|
TUNE_SDIO_PLUS, &autok_host_para);
|
||
|
if ((ret & E_RES_CMD_TMO) != 0) {
|
||
|
tune_tmo_cnt[tune_tx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_tx_value);
|
||
|
} else if ((ret&(E_RES_RSP_CRC)) != 0) {
|
||
|
tune_crc_cnt[tune_tx_value]++;
|
||
|
Rx64 |= (u64) (1LL << tune_tx_value);
|
||
|
} else if ((ret & (E_RES_PASS)) == 0)
|
||
|
tune_pass_cnt[tune_tx_value]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if ((tune_tmo_cnt[i] != 0) || (tune_crc_cnt[i] != 0))
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_pass_cnt[i] == TUNE_TX_CNT)
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune_cmd_TX 0 - 31 %s\r\n", tune_result);
|
||
|
Rx64 |= 0xffffffff00000000;
|
||
|
autok_check_scan_res64_new(Rx64, &pInfo->scan_info[0], 0);
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &cmd_tx_sel);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune host cmd TX sel:%d\r\n", cmd_tx_sel);
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx_sel, AUTOK_WRITE);
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune cmd TX]======end======\r\n");
|
||
|
if (res != NULL) {
|
||
|
autok_window_apply(H_CMD_TX, Rx64, res);
|
||
|
autok_param_update(EMMC50_CMD_TX_DLY, cmd_tx_sel, res);
|
||
|
}
|
||
|
|
||
|
/* restore cmd tx setting */
|
||
|
autok_adjust_param(host, EMMC50_CMD_TX_DLY,
|
||
|
&cmd_tx, AUTOK_WRITE);
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_DAT_H_TX_ENABLE
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune data TX]======start======\r\n");
|
||
|
separate_tune_dat_tx:
|
||
|
AUTOK_RAWPRINT("[AUTOK][separate tune data TX]======start======\r\n");
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
/* restore device cmd rx setting */
|
||
|
autok_msdc_device_rx_set(host, cmd_rx & 0x1f, 0, 0);
|
||
|
AUTOK_RAWPRINT("[AUTOK][dev RX set]CMD:%d D0-3:%d D4-7:%d\r\n",
|
||
|
cmd_rx, 0, 0);
|
||
|
}
|
||
|
/* Step2 : Tuning Data TX */
|
||
|
Rx64 = 0LL;
|
||
|
Rx64_separate[0] = 0LL;
|
||
|
Rx64_separate[1] = 0LL;
|
||
|
Rx64_separate[2] = 0LL;
|
||
|
Rx64_separate[3] = 0LL;
|
||
|
Rx64_separate[4] = 0LL;
|
||
|
Rx64_separate[5] = 0LL;
|
||
|
Rx64_separate[6] = 0LL;
|
||
|
Rx64_separate[7] = 0LL;
|
||
|
|
||
|
if (separate_tune_start == 1) {
|
||
|
if (host->hw->host_function == MSDC_EMMC)
|
||
|
separate_tune_cnt = 8;
|
||
|
else
|
||
|
separate_tune_cnt = 4;
|
||
|
}
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
if (platform_para_misc.emmc_data_tx_tune == 1)
|
||
|
opcode = MMC_WRITE_MULTIPLE_BLOCK;
|
||
|
else if (platform_para_misc.emmc_data_tx_tune == 0)
|
||
|
opcode = MMC_WRITE_BLOCK;
|
||
|
}
|
||
|
for (j = 0; j < separate_tune_cnt; j++) {
|
||
|
Rx64 = 0LL;
|
||
|
for (tune_tx_value = 0; tune_tx_value < 32; tune_tx_value++) {
|
||
|
tmo_cnt = 0;
|
||
|
crc_cnt = 0;
|
||
|
pass_cnt = 0;
|
||
|
if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (separate_tune_start != 0)) {
|
||
|
switch (j) {
|
||
|
case 0:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA0_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 1:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA1_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 2:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA2_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 3:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA3_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 4:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA4_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 5:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA5_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 6:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA6_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 7:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA7_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else if ((host->hw->host_function == MSDC_EMMC)
|
||
|
&& (separate_tune_start == 0)) {
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA0_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA1_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA2_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA3_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA4_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA5_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA6_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA7_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
} else if ((host->hw->host_function != MSDC_EMMC)
|
||
|
&& (separate_tune_start == 1)) {
|
||
|
switch (j) {
|
||
|
case 0:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA0_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 1:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA1_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 2:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA2_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 3:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA3_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else if ((host->hw->host_function != MSDC_EMMC)
|
||
|
&& (separate_tune_start == 0)) {
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA0_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA1_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA2_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA3_TX_DLY,
|
||
|
&tune_tx_value, AUTOK_WRITE);
|
||
|
}
|
||
|
if (autok_tune_data_tx(host, &autok_host_para,
|
||
|
opcode, tune_tx_value,
|
||
|
&Rx64, &crc_cnt,
|
||
|
&tmo_cnt,
|
||
|
&pass_cnt) != 0)
|
||
|
goto end;
|
||
|
if (separate_tune_start)
|
||
|
Rx64_separate[j] = Rx64;
|
||
|
tune_crc_cnt[tune_tx_value] = crc_cnt;
|
||
|
tune_tmo_cnt[tune_tx_value] = tmo_cnt;
|
||
|
tune_pass_cnt[tune_tx_value] = pass_cnt;
|
||
|
}
|
||
|
/* print result */
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
if ((tune_tmo_cnt[i] != 0) || (tune_crc_cnt[i] != 0))
|
||
|
tune_result[i] = 'X';
|
||
|
else if (tune_pass_cnt[i] == TUNE_TX_CNT)
|
||
|
tune_result[i] = 'O';
|
||
|
}
|
||
|
tune_result[32] = '\0';
|
||
|
if (separate_tune_start == 0)
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX 0 - 31 %s\r\n",
|
||
|
tune_result);
|
||
|
else
|
||
|
AUTOK_RAWPRINT("[AUTOK]DAT TX(%d) 0 - 31 %s\r\n",
|
||
|
j, tune_result);
|
||
|
if (separate_tune_start == 1) {
|
||
|
switch (j) {
|
||
|
case 0:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 1:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 2:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 3:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 4:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA4_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 5:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA5_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 6:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA6_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
case 7:
|
||
|
autok_adjust_param(host,
|
||
|
EMMC50_DATA7_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* restore data tx setting */
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&dat_tx[4], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&dat_tx[5], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&dat_tx[6], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&dat_tx[7], AUTOK_WRITE);
|
||
|
} else {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx[3], AUTOK_WRITE);
|
||
|
}
|
||
|
if (separate_tune_start == 1) {
|
||
|
if (host->hw->host_function == MSDC_EMMC)
|
||
|
separate_tune_cnt = 8;
|
||
|
else
|
||
|
separate_tune_cnt = 4;
|
||
|
for (j = 0; j < separate_tune_cnt; j++) {
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
Rx64_separate[j] |= 0xffffffff00000000;
|
||
|
autok_check_scan_res64_new(Rx64_separate[j],
|
||
|
&pInfo->scan_info[0], 0);
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0],
|
||
|
&dat_tx_separ_sel[j]);
|
||
|
AUTOK_RAWPRINT(
|
||
|
"[AUTOK]separate tune host data%d TX sel:%d\r\n",
|
||
|
j, dat_tx_separ_sel[j]);
|
||
|
}
|
||
|
} else {
|
||
|
Rx64 |= 0xffffffff00000000;
|
||
|
autok_check_scan_res64_new(Rx64, &pInfo->scan_info[0], 0);
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[0], &dat_tx_sel);
|
||
|
AUTOK_RAWPRINT("[AUTOK]tune host data TX sel:%d\r\n",
|
||
|
dat_tx_sel);
|
||
|
}
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
if (separate_tune_start == 1) {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx_separ_sel[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx_separ_sel[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx_separ_sel[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx_separ_sel[3], AUTOK_WRITE);
|
||
|
} else {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
}
|
||
|
} else {
|
||
|
if (separate_tune_start == 1) {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx_separ_sel[0], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx_separ_sel[1], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx_separ_sel[2], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx_separ_sel[3], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&dat_tx_separ_sel[4], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&dat_tx_separ_sel[5], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&dat_tx_separ_sel[6], AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&dat_tx_separ_sel[7], AUTOK_WRITE);
|
||
|
} else {
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&dat_tx_sel, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
AUTOK_RAWPRINT("[AUTOK][tune data TX]=========end========\r\n");
|
||
|
if (res != NULL) {
|
||
|
autok_window_apply(H_DATA_TX, Rx64, res);
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
if (separate_tune_start == 1) {
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
dat_tx_separ_sel[0], res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
dat_tx_separ_sel[1], res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
dat_tx_separ_sel[2], res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
dat_tx_separ_sel[3], res);
|
||
|
} else {
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
}
|
||
|
} else {
|
||
|
if (separate_tune_start == 1) {
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
dat_tx_separ_sel[0], res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
dat_tx_separ_sel[1], res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
dat_tx_separ_sel[2], res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
dat_tx_separ_sel[3], res);
|
||
|
autok_param_update(EMMC50_DATA4_TX_DLY,
|
||
|
dat_tx_separ_sel[4], res);
|
||
|
autok_param_update(EMMC50_DATA5_TX_DLY,
|
||
|
dat_tx_separ_sel[5], res);
|
||
|
autok_param_update(EMMC50_DATA6_TX_DLY,
|
||
|
dat_tx_separ_sel[6], res);
|
||
|
autok_param_update(EMMC50_DATA7_TX_DLY,
|
||
|
dat_tx_separ_sel[7], res);
|
||
|
} else {
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA4_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA5_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA6_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
autok_param_update(EMMC50_DATA7_TX_DLY,
|
||
|
dat_tx_sel, res);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (host->hw->host_function == MSDC_SDIO) {
|
||
|
/* restore data tx setting */
|
||
|
autok_msdc_device_rx_set(host, cmd_rx & 0x1f,
|
||
|
data_p_rx & 0x1f, data_n_rx & 0x1f);
|
||
|
AUTOK_RAWPRINT("[AUTOK][dev RX set]CMD:%d D0-3:%d D4-7:%d\r\n",
|
||
|
cmd_rx, data_p_rx, data_n_rx);
|
||
|
}
|
||
|
if (separate_tune_start == 1)
|
||
|
separate_tune_start = 0;
|
||
|
else {
|
||
|
if (platform_para_misc.data_tx_separate_tune == 1) {
|
||
|
separate_tune_start = 1;
|
||
|
goto separate_tune_dat_tx;
|
||
|
}
|
||
|
}
|
||
|
end:
|
||
|
#endif
|
||
|
kfree(pInfo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int autok_sdio30_plus_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
u8 autok_tune_res[TUNING_PARAM_COUNT];
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
unsigned int dvfs_en = 0;
|
||
|
unsigned int dvfs_hw = 0;
|
||
|
unsigned int dtoc = 0;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, dvfs_en);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, dvfs_hw);
|
||
|
MSDC_GET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, 0);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, 0);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, 3);
|
||
|
|
||
|
/* store pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
autok_adjust_param(host, i, &value, AUTOK_READ);
|
||
|
autok_tune_res[i] = value;
|
||
|
}
|
||
|
|
||
|
ret = execute_online_tuning_sdio30_plus(host, res);
|
||
|
if (ret != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======Autok Failed======\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======restore pre paras======\r\n");
|
||
|
/* restore pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
value = (u8) autok_tune_res[i];
|
||
|
autok_adjust_param(host, i, &value, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
#if AUTOK_SDIO_OFFLINE_TUNE_TX_ENABLE
|
||
|
autok_offline_tuning_TX(host, res);
|
||
|
#endif
|
||
|
#if AUTOK_OFFLINE_TUNE_DEVICE_RX_ENABLE
|
||
|
autok_offline_tuning_device_RX(host, res);
|
||
|
#endif
|
||
|
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, dvfs_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, dvfs_hw);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_sdio30_plus_tuning);
|
||
|
|
||
|
int autok_execute_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
u8 autok_tune_res[TUNING_PARAM_COUNT];
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
unsigned int dvfs_en = 0;
|
||
|
unsigned int dvfs_hw = 0;
|
||
|
unsigned int dtoc = 0;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, dvfs_en);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, dvfs_hw);
|
||
|
MSDC_GET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, 0);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, 0);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, 3);
|
||
|
|
||
|
/* store pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
autok_adjust_param(host, i, &value, AUTOK_READ);
|
||
|
autok_tune_res[i] = value;
|
||
|
}
|
||
|
|
||
|
ret = execute_online_tuning(host, res);
|
||
|
if (ret != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======Autok Failed======\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======restore pre paras======\r\n");
|
||
|
/* restore pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
value = (u8) autok_tune_res[i];
|
||
|
autok_adjust_param(host, i, &value, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
#if AUTOK_SD_CARD_OFFLINE_TUNE_TX_ENABLE
|
||
|
if (host->hw->host_function == MSDC_SD)
|
||
|
autok_offline_tuning_clk_TX(host, MMC_WRITE_BLOCK);
|
||
|
#endif
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_EN, dvfs_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_DVFS_HW, dvfs_hw);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_execute_tuning);
|
||
|
|
||
|
int hs400_execute_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
u8 autok_tune_res[TUNING_PARAM_COUNT];
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
|
||
|
/* store pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
autok_adjust_param(host, i, &value, AUTOK_READ);
|
||
|
autok_tune_res[i] = value;
|
||
|
}
|
||
|
|
||
|
ret = execute_online_tuning_hs400(host, res);
|
||
|
if (ret != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======HS400 Failed======\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======restore pre paras======\r\n");
|
||
|
/* restore pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
value = (u8) autok_tune_res[i];
|
||
|
autok_adjust_param(host, i, &value, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
#if AUTOK_EMMC_OFFLINE_TUNE_TX_ENABLE
|
||
|
autok_offline_tuning_TX(host, res);
|
||
|
#endif
|
||
|
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK][HS400]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(hs400_execute_tuning);
|
||
|
|
||
|
int hs400_execute_tuning_cmd(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
|
||
|
autok_init_hs400(host);
|
||
|
ret = execute_cmd_online_tuning(host, res);
|
||
|
if (ret != 0)
|
||
|
AUTOK_RAWPRINT("[AUTOK cmd] ======HS400 Failed======\r\n");
|
||
|
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK][HS400 cmd]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(hs400_execute_tuning_cmd);
|
||
|
|
||
|
int hs200_execute_tuning(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
u8 autok_tune_res[TUNING_PARAM_COUNT];
|
||
|
unsigned int i = 0;
|
||
|
unsigned int value = 0;
|
||
|
unsigned int dtoc = 0;
|
||
|
struct AUTOK_PLAT_FUNC platform_para_func;
|
||
|
unsigned int ckgen;
|
||
|
|
||
|
memset(&platform_para_func, 0, sizeof(struct AUTOK_PLAT_FUNC));
|
||
|
get_platform_func(platform_para_func);
|
||
|
do_gettimeofday(&tm_s);
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_GET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, 3);
|
||
|
|
||
|
/* store pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
autok_adjust_param(host, i, &value, AUTOK_READ);
|
||
|
autok_tune_res[i] = value;
|
||
|
}
|
||
|
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
if (platform_para_func.latch_enhance == 1) {
|
||
|
ckgen = 0;
|
||
|
autok_write_param(host, CKGEN_MSDC_DLY_SEL, ckgen);
|
||
|
}
|
||
|
ret = execute_online_tuning_hs200(host, res);
|
||
|
if (platform_para_func.latch_enhance == 1) {
|
||
|
if (ret == -2) {
|
||
|
ckgen += 1;
|
||
|
autok_write_param(host, CKGEN_MSDC_DLY_SEL, ckgen);
|
||
|
ret = execute_online_tuning_hs200(host, res);
|
||
|
}
|
||
|
}
|
||
|
if (ret != 0) {
|
||
|
AUTOK_RAWPRINT("[AUTOK] ======Autok HS200 Failed======\r\n");
|
||
|
AUTOK_RAWPRINT("[AUTOK]======restore pre paras======\r\n");
|
||
|
/* restore pre autok parameter */
|
||
|
for (i = 0; i < TUNING_PARAM_COUNT; i++) {
|
||
|
value = (u8) autok_tune_res[i];
|
||
|
autok_adjust_param(host, i, &value, AUTOK_WRITE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
autok_msdc_reset();
|
||
|
msdc_clear_fifo();
|
||
|
MSDC_WRITE32(MSDC_INT, 0xffffffff);
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(SDC_CFG, SDC_CFG_DTOC, dtoc);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK][HS200]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(hs200_execute_tuning);
|
||
|
|
||
|
int hs200_execute_tuning_cmd(struct msdc_host *host, u8 *res)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int clk_pwdn = 0;
|
||
|
unsigned int int_en = 0;
|
||
|
void __iomem *base = host->base;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
int_en = MSDC_READ32(MSDC_INTEN);
|
||
|
MSDC_WRITE32(MSDC_INTEN, 0);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, 1);
|
||
|
|
||
|
autok_init_hs200(host);
|
||
|
ret = execute_cmd_online_tuning(host, res);
|
||
|
if (ret != 0)
|
||
|
AUTOK_RAWPRINT("[AUTOK cmd] ======HS200 Failed======\r\n");
|
||
|
|
||
|
MSDC_WRITE32(MSDC_INTEN, int_en);
|
||
|
MSDC_SET_FIELD(MSDC_CFG, MSDC_CFG_CKPDN, clk_pwdn);
|
||
|
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK][HS200 cmd]======Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(hs200_execute_tuning_cmd);
|
||
|
|
||
|
int autok_vcore_merge_sel(struct msdc_host *host, unsigned int merge_cap)
|
||
|
{
|
||
|
void __iomem *base = host->base;
|
||
|
unsigned int ret = 0;
|
||
|
struct timeval tm_s, tm_e;
|
||
|
unsigned int tm_val = 0;
|
||
|
unsigned int uCmdEdge = 0;
|
||
|
unsigned int uDatEdge = 0;
|
||
|
u64 RawData64 = 0LL;
|
||
|
unsigned int RawData = 0;
|
||
|
unsigned int j, k;
|
||
|
struct AUTOK_REF_INFO_NEW *pInfo;
|
||
|
unsigned int max_win[2];
|
||
|
unsigned int dly_sel[2];
|
||
|
char tune_result_str64[65];
|
||
|
char tune_result_str[33];
|
||
|
unsigned int score = 0;
|
||
|
unsigned int data_dly = 0;
|
||
|
unsigned int clk_mode = 0;
|
||
|
|
||
|
do_gettimeofday(&tm_s);
|
||
|
MSDC_GET_FIELD(MSDC_CFG, MSDC_CFG_CKMOD, clk_mode);
|
||
|
|
||
|
pInfo = kmalloc(sizeof(struct AUTOK_REF_INFO_NEW), GFP_ATOMIC);
|
||
|
if (!pInfo) {
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK] mem alloc fail\r\n");
|
||
|
return -1;
|
||
|
}
|
||
|
/* Init set window 0xFF as infinite */
|
||
|
for (j = CMD_MAX_WIN; j <= H_CLK_TX_MAX_WIN; j++)
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][j] = 0xFF;
|
||
|
|
||
|
/* Step1 : Cmd Path */
|
||
|
if (!(merge_cap & MERGE_CMD))
|
||
|
goto data_merge;
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
|
||
|
uCmdEdge = 0;
|
||
|
do {
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < (AUTOK_VCORE_NUM); j++) {
|
||
|
for (k = 0; k < 8; k++) {
|
||
|
if (uCmdEdge)
|
||
|
RawData64 |=
|
||
|
(((u64)host->autok_res[j][CMD_SCAN_F0 + k])
|
||
|
<< (8 * k));
|
||
|
else
|
||
|
RawData64 |=
|
||
|
(((u64)host->autok_res[j][CMD_SCAN_R0 + k])
|
||
|
<< (8 * k));
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]CMD %d \t %d \t %s merge\r\n",
|
||
|
uCmdEdge, score, tune_result_str64);
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[uCmdEdge], 0) != 0)
|
||
|
goto fail;
|
||
|
max_win[uCmdEdge] =
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[uCmdEdge],
|
||
|
&dly_sel[uCmdEdge]);
|
||
|
if (uCmdEdge)
|
||
|
autok_window_apply(CMD_FALL, RawData64,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
else
|
||
|
autok_window_apply(CMD_RISE, RawData64,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
uCmdEdge ^= 0x1;
|
||
|
} while (uCmdEdge);
|
||
|
if (max_win[0] >= max_win[1]) {
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt = dly_sel[0];
|
||
|
} else {
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
pInfo->opt_dly_cnt = dly_sel[1];
|
||
|
}
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]cmd edge = %d cmd dly = %d max win = %d\r\n",
|
||
|
pInfo->opt_edge_sel,
|
||
|
pInfo->opt_dly_cnt,
|
||
|
max_win[pInfo->opt_edge_sel]);
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][CMD_MAX_WIN] =
|
||
|
max_win[pInfo->opt_edge_sel];
|
||
|
autok_param_update(CMD_EDGE, pInfo->opt_edge_sel,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_paddly_update(CMD_PAD_RDLY, pInfo->opt_dly_cnt,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_adjust_param(host, CMD_EDGE,
|
||
|
&pInfo->opt_edge_sel, AUTOK_WRITE);
|
||
|
autok_adjust_paddly(host, &pInfo->opt_dly_cnt, CMD_PAD_RDLY);
|
||
|
/* Step2 : Dat Path */
|
||
|
data_merge:
|
||
|
if (clk_mode == 3) {
|
||
|
data_dly = 0;
|
||
|
for (j = 0; j < AUTOK_VCORE_NUM; j++)
|
||
|
data_dly += host->autok_res[j][DAT_RD_D_DLY1];
|
||
|
data_dly = data_dly / AUTOK_VCORE_NUM;
|
||
|
autok_paddly_update(DAT_PAD_RDLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_adjust_paddly(host, &data_dly, DAT_PAD_RDLY);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]dat dly = %d\r\n", data_dly);
|
||
|
goto ds_merge;
|
||
|
}
|
||
|
if (!(merge_cap & MERGE_DAT))
|
||
|
goto ds_merge;
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
uDatEdge = 0;
|
||
|
do {
|
||
|
RawData64 = 0LL;
|
||
|
for (j = 0; j < AUTOK_VCORE_NUM; j++) {
|
||
|
for (k = 0; k < 8; k++) {
|
||
|
if (uDatEdge)
|
||
|
RawData64 |=
|
||
|
(((u64)host->autok_res[j][DAT_SCAN_F0 + k])
|
||
|
<< (8 * k));
|
||
|
else
|
||
|
RawData64 |=
|
||
|
(((u64)host->autok_res[j][DAT_SCAN_R0 + k])
|
||
|
<< (8 * k));
|
||
|
}
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]DAT %d \t %d \t %s merge\r\n",
|
||
|
uDatEdge, score, tune_result_str64);
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[uDatEdge], 0) != 0)
|
||
|
goto fail;
|
||
|
max_win[uDatEdge] =
|
||
|
autok_ds_dly_sel(&pInfo->scan_info[uDatEdge],
|
||
|
&dly_sel[uDatEdge]);
|
||
|
if (uDatEdge)
|
||
|
autok_window_apply(DAT_FALL, RawData64,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
else
|
||
|
autok_window_apply(DAT_RISE, RawData64,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
uDatEdge ^= 0x1;
|
||
|
} while (uDatEdge);
|
||
|
if (max_win[0] >= max_win[1]) {
|
||
|
pInfo->opt_edge_sel = 0;
|
||
|
pInfo->opt_dly_cnt = dly_sel[0];
|
||
|
} else {
|
||
|
pInfo->opt_edge_sel = 1;
|
||
|
pInfo->opt_dly_cnt = dly_sel[1];
|
||
|
}
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES,
|
||
|
"[AUTOK]dat edge = %d dat dly = %d max win = %d\r\n",
|
||
|
pInfo->opt_edge_sel, pInfo->opt_dly_cnt,
|
||
|
max_win[pInfo->opt_edge_sel]);
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][DAT_MAX_WIN]
|
||
|
= max_win[pInfo->opt_edge_sel];
|
||
|
autok_param_update(RD_FIFO_EDGE, pInfo->opt_edge_sel,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_paddly_update(DAT_PAD_RDLY, pInfo->opt_dly_cnt,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(WD_FIFO_EDGE, pInfo->opt_edge_sel,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_adjust_param(host, RD_FIFO_EDGE,
|
||
|
&pInfo->opt_edge_sel, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, WD_FIFO_EDGE,
|
||
|
&pInfo->opt_edge_sel, AUTOK_WRITE);
|
||
|
autok_adjust_paddly(host,
|
||
|
&pInfo->opt_dly_cnt, DAT_PAD_RDLY);
|
||
|
/* Step3 : DS Path */
|
||
|
ds_merge:
|
||
|
if (!(merge_cap & MERGE_DS_DAT))
|
||
|
goto device_data_rx_merge;
|
||
|
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][EMMC50_DS_ZDLY_DLY] =
|
||
|
host->autok_res[AUTOK_VCORE_LEVEL0][EMMC50_DS_ZDLY_DLY];
|
||
|
|
||
|
RawData64 = 0LL;
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
for (j = 0; j < AUTOK_VCORE_NUM; j++) {
|
||
|
for (k = 0; k < 8; k++)
|
||
|
RawData64 |=
|
||
|
(((u64)host->autok_res[j][DS_DAT_SCAN_0 + k])
|
||
|
<< (8 * k));
|
||
|
}
|
||
|
score = autok_simple_score64(tune_result_str64, RawData64);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DLY1/2 \t %d \t %s merge\r\n",
|
||
|
score, tune_result_str64);
|
||
|
|
||
|
autok_window_apply(DS_DATA_WIN, RawData64,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
max_win[0] = autok_ds_dly_sel(&pInfo->scan_info[0], &data_dly);
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][DS_MAX_WIN] = max_win[0];
|
||
|
autok_paddly_update(DS_PAD_RDLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_adjust_paddly(host, &data_dly, DS_PAD_RDLY);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DS dly = %d\r\n", data_dly);
|
||
|
/* Step4 : Device Dat RX */
|
||
|
device_data_rx_merge:
|
||
|
if (!(merge_cap & MERGE_DEVICE_D_RX))
|
||
|
goto host_data_tx_merge;
|
||
|
RawData64 = 0LL;
|
||
|
RawData = 0;
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
for (j = 0; j < AUTOK_VCORE_NUM; j++) {
|
||
|
for (k = 0; k < 4; k++)
|
||
|
RawData |= ((host->autok_res[j][D_DATA_SCAN_0 + k])
|
||
|
<< (8 * k));
|
||
|
}
|
||
|
score = autok_simple_score(tune_result_str, RawData);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]dev DAT RX \t %d \t %s merge\r\n",
|
||
|
score, tune_result_str);
|
||
|
autok_window_apply(D_DATA_RX, RawData,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
RawData64 = ((u64)RawData) | 0xffffffff00000000;
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
max_win[0] = autok_ds_dly_sel(&pInfo->scan_info[0], &data_dly);
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][DEV_D_RX_MAX_WIN] = max_win[0];
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]dev DAT RX dly = %d\r\n",
|
||
|
data_dly);
|
||
|
/* Step5 : Dat TX */
|
||
|
host_data_tx_merge:
|
||
|
if (!(merge_cap & MERGE_HOST_D_TX))
|
||
|
goto end;
|
||
|
RawData64 = 0LL;
|
||
|
RawData = 0;
|
||
|
memset(pInfo, 0, sizeof(struct AUTOK_REF_INFO_NEW));
|
||
|
for (j = 0; j < AUTOK_VCORE_NUM; j++) {
|
||
|
for (k = 0; k < 4; k++)
|
||
|
RawData |= ((host->autok_res[j][H_DATA_SCAN_0 + k])
|
||
|
<< (8 * k));
|
||
|
}
|
||
|
score = autok_simple_score(tune_result_str, RawData);
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]DAT TX \t %d \t %s merge\r\n",
|
||
|
score, tune_result_str);
|
||
|
autok_window_apply(H_DATA_TX, RawData,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
RawData64 = ((u64)RawData) | 0xffffffff00000000;
|
||
|
if (autok_check_scan_res64_new(RawData64,
|
||
|
&pInfo->scan_info[0], 0) != 0)
|
||
|
goto fail;
|
||
|
|
||
|
max_win[0] = autok_ds_dly_sel(&pInfo->scan_info[0], &data_dly);
|
||
|
host->autok_res[AUTOK_VCORE_MERGE][H_D_TX_MAX_WIN] = max_win[0];
|
||
|
autok_param_update(EMMC50_DATA0_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA1_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA2_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA3_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
autok_param_update(EMMC50_DATA4_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA5_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA6_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
autok_param_update(EMMC50_DATA7_TX_DLY, data_dly,
|
||
|
host->autok_res[AUTOK_VCORE_MERGE]);
|
||
|
}
|
||
|
autok_adjust_param(host, EMMC50_DATA0_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA1_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA2_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA3_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
if (host->hw->host_function == MSDC_EMMC) {
|
||
|
autok_adjust_param(host, EMMC50_DATA4_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA5_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA6_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
autok_adjust_param(host, EMMC50_DATA7_TX_DLY,
|
||
|
&data_dly, AUTOK_WRITE);
|
||
|
}
|
||
|
AUTOK_DBGPRINT(AUTOK_DBG_RES, "[AUTOK]dat tx = %d\r\n", data_dly);
|
||
|
|
||
|
end:
|
||
|
do_gettimeofday(&tm_e);
|
||
|
tm_val = (tm_e.tv_sec - tm_s.tv_sec) * 1000
|
||
|
+ (tm_e.tv_usec - tm_s.tv_usec) / 1000;
|
||
|
AUTOK_RAWPRINT("[AUTOK][merge]======Time Cost:%d ms======\r\n", tm_val);
|
||
|
|
||
|
kfree(pInfo);
|
||
|
return ret;
|
||
|
fail:
|
||
|
kfree(pInfo);
|
||
|
return -1;
|
||
|
}
|
||
|
EXPORT_SYMBOL(autok_vcore_merge_sel);
|
||
|
|