From 366ccec376aa0b65e113dcf1cfa428ec46788720 Mon Sep 17 00:00:00 2001 From: Ruixuan Li Date: Sat, 12 Oct 2019 20:45:18 +0800 Subject: [PATCH] emmc: save tuning parameter of hs400 [1/1] PD#SWPL-15199 Problem: tunig process of hs400 is too slow Solution: saved tuning parameter of hs400 on emmc Verify: passed on tm2_t962e2_ab311 Change-Id: Ifd1ba375dd93745c9db33a94f3480ebfc21e999f Signed-off-by: Ruixuan Li Signed-off-by: chunlong.cao --- arch/arm/boot/dts/amlogic/mesontm2.dtsi | 1 + arch/arm64/boot/dts/amlogic/mesontm2.dtsi | 1 + drivers/amlogic/mmc/aml_sd_emmc_v3.c | 190 +++++++++++++++++++++- drivers/amlogic/mmc/amlsd_of.c | 8 +- drivers/amlogic/mmc/emmc_partitions.c | 35 ++++ drivers/mmc/core/mmc.c | 3 + drivers/thermal/thermal_core.c | 25 +++ drivers/thermal/thermal_core.h | 3 + include/linux/amlogic/sd.h | 31 ++++ include/linux/mmc/emmc_partitions.h | 2 + include/linux/mmc/host.h | 2 + 11 files changed, 291 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/amlogic/mesontm2.dtsi b/arch/arm/boot/dts/amlogic/mesontm2.dtsi index 8b3dd3f61ec6..f41683c08de1 100644 --- a/arch/arm/boot/dts/amlogic/mesontm2.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontm2.dtsi @@ -1411,6 +1411,7 @@ ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ /*caps defined in dts*/ tx_delay = <0>; + save_para = <0>; max_req_size = <0x20000>; /**128KB*/ gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>; hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; diff --git a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi index 06dd42216af4..19cc91f80b55 100644 --- a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi @@ -1410,6 +1410,7 @@ ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ /*caps defined in dts*/ tx_delay = <0>; + save_para = <0>; max_req_size = <0x20000>; /**128KB*/ gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>; hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; diff --git a/drivers/amlogic/mmc/aml_sd_emmc_v3.c b/drivers/amlogic/mmc/aml_sd_emmc_v3.c index a731644042ef..f1cc617c31b6 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc_v3.c +++ b/drivers/amlogic/mmc/aml_sd_emmc_v3.c @@ -43,6 +43,7 @@ #include #include #include +#include "../../thermal/thermal_core.h" int aml_fixdiv_calc(unsigned int *fixdiv, struct clock_lay_t *clk) { @@ -706,6 +707,9 @@ static int aml_sd_emmc_cali_v3(struct mmc_host *mmc, cmd.arg = MMC_RANDOM_OFFSET; else if (!strcmp(pattern, MMC_DTB_NAME)) cmd.arg = MMC_DTB_OFFSET; + else if (!strcmp(pattern, MMC_TUNING_NAME)) + cmd.arg = MMC_TUNING_OFFSET; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; stop.opcode = MMC_STOP_TRANSMISSION; @@ -1132,14 +1136,14 @@ static u32 scan_emmc_cmd_win(struct mmc_host *mmc, int send_status) for (j = 0; j < repeat_times; j++) { if (send_status) err = emmc_send_cmd(mmc, - MMC_SEND_STATUS, - 1 << 16, - MMC_RSP_R1 | MMC_CMD_AC); + MMC_SEND_STATUS, + 1 << 16, + MMC_RSP_R1 | MMC_CMD_AC); else err = single_read_cmd_for_scan(mmc, - MMC_READ_SINGLE_BLOCK, - host->blk_test, 512, 1, - offset); + MMC_READ_SINGLE_BLOCK, + host->blk_test, 512, 1, + offset); if (!err) str[i]++; else @@ -1302,6 +1306,8 @@ static int emmc_ds_manual_sht(struct mmc_host *mmc) host->cmd_retune = 1; host->find_win = 1; } + + gintf3->ds_sht_m = 0; for (i = 0; i < 64; i++) { host->is_tunning = 1; err = emmc_test_bus(mmc); @@ -1365,7 +1371,6 @@ static int emmc_ds_manual_sht(struct mmc_host *mmc) static void aml_emmc_hs400_general(struct mmc_host *mmc) { - update_all_line_eyetest(mmc); emmc_ds_core_align(mmc); update_all_line_eyetest(mmc); @@ -2325,17 +2330,188 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode) readl(host->base + SD_EMMC_INTF3)); return err; } + +static long long _para_checksum_calc(struct aml_tuning_para *para) +{ + int i = 0; + int size = sizeof(struct aml_tuning_para) - 6 * sizeof(unsigned int); + unsigned int *buffer; + long long checksum = 0; + + if (!para) + return 1; + + size = size >> 2; + buffer = (unsigned int *)para; + while (i < size) + checksum += buffer[i++]; + + return checksum; +} + +/* + * read tuning para from reserved partition + * and copy it to pdata->para + */ +int aml_read_tuning_para(struct mmc_host *mmc) +{ + int off, blk; + int ret; + int para_size; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + + if (pdata->save_para == 0) + return 0; + + para_size = sizeof(struct aml_tuning_para); + blk = (para_size - 1) / 512 + 1; + off = MMC_TUNING_OFFSET; + + if (blk == 1) + ret = single_read_cmd_for_scan(mmc, + MMC_READ_SINGLE_BLOCK, + host->blk_test, 512, + blk, off); + else + ret = aml_sd_emmc_cali_v3(mmc, + MMC_READ_MULTIPLE_BLOCK, + host->blk_test, 512, blk, + MMC_TUNING_NAME); + if (ret) { + pr_info("read tuning parameter failed\n"); + return ret; + } + + memcpy(&pdata->para, host->blk_test, para_size); + return ret; +} + +/*set para on controller register*/ +static void aml_set_tuning_para(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + struct aml_tuning_para *para = &pdata->para; + int temp_index; + u32 delay1, delay2, intf3; + + temp_index = para->temperature / 10000; + delay1 = pdata->para.hs4[temp_index].delay1; + delay2 = pdata->para.hs4[temp_index].delay2; + intf3 = pdata->para.hs4[temp_index].intf3; + + writel(delay1, host->base + SD_EMMC_DELAY1_V3); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + writel(intf3, host->base + SD_EMMC_INTF3); +} + +/*save parameter on mmc_host pdata*/ +static void aml_save_tuning_para(struct mmc_host *mmc) +{ + unsigned int checksum; + int temp_index; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + struct aml_tuning_para *para = &pdata->para; + + u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3); + u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + u32 intf3 = readl(host->base + SD_EMMC_INTF3); + + if (pdata->save_para == 0) + return; + + temp_index = para->temperature / 10000; + if (para->temperature < 0 || temp_index > 6) { + para->update = 0; + return; + } + + pdata->para.hs4[temp_index].delay1 = delay1; + pdata->para.hs4[temp_index].delay2 = delay2; + pdata->para.hs4[temp_index].intf3 = intf3; + pdata->para.hs4[temp_index].flag = 1; + pdata->para.magic = 0x00487e44; /*E~K\0*/ + pdata->para.version = 1; + + checksum = _para_checksum_calc(para); + pdata->para.checksum = checksum; +} + +/* + * check if tuning parameter is exist + * check if temperature is in the 0~69 + * check if the parameter has been tuning + * under the current temperature + * check if the data had been broken by checksum + * + * if all four condition above is yes, the tuning parameter + * could be use directly + * otherwise retunning and save parameter + */ +static int aml_para_is_exist(struct mmc_host *mmc) +{ + int temperature; + int temp_index; + unsigned int checksum; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct aml_tuning_para *para = &pdata->para; + + if (pdata->save_para == 0) + return 0; + + temperature = thermal_get_temp_by_index(0); + if (temperature == -1) { + para->update = 0; + pr_info("get temperature failed\n"); + return 0; + } + + pr_info("current temperature is %d\n", temperature); + temp_index = temperature / 10000; + para->temperature = temperature; + para->update = 1; + /* temperature range is 0 ~ 69 */ + if (temperature < 0 || temp_index > 6) { + pr_info("temperature is out of normal range\n"); + return 0; + } + + if (para->hs4[temp_index].flag == 0) { + pr_info("current temperature %d degree not tuning yet\n", + temperature / 1000); + return 0; + } + + checksum = _para_checksum_calc(para); + if (checksum != para->checksum) { + pr_info("warning: checksum is not match\n"); + return 0; + } + para->update = 0; + + return 1; +} + int aml_post_hs400_timming(struct mmc_host *mmc) { struct amlsd_platform *pdata = mmc_priv(mmc); struct amlsd_host *host = pdata->host; + aml_sd_emmc_clktest(mmc); + if (aml_para_is_exist(mmc)) { + aml_set_tuning_para(mmc); + return 0; + } if (host->data->chip_type == MMC_CHIP_G12B) aml_emmc_hs400_Revb(mmc); else if (host->data->chip_type >= MMC_CHIP_TL1) aml_emmc_hs400_tl1(mmc); else aml_emmc_hs400_general(mmc); + + aml_save_tuning_para(mmc); return 0; } diff --git a/drivers/amlogic/mmc/amlsd_of.c b/drivers/amlogic/mmc/amlsd_of.c index 3803142510fd..d64a16726012 100644 --- a/drivers/amlogic/mmc/amlsd_of.c +++ b/drivers/amlogic/mmc/amlsd_of.c @@ -204,11 +204,13 @@ int amlsd_get_platform_data(struct platform_device *pdev, SD_PARSE_U32_PROP_DEC(child, "card_type", prop, pdata->card_type); SD_PARSE_U32_PROP_DEC(child, "tx_delay", - prop, pdata->tx_delay); + prop, pdata->tx_delay); + SD_PARSE_U32_PROP_DEC(child, "save_para", + prop, pdata->save_para); SD_PARSE_U32_PROP_DEC(child, "latest_dat", - prop, pdata->latest_dat); + prop, pdata->latest_dat); SD_PARSE_U32_PROP_DEC(child, "co_phase", - prop, pdata->co_phase); + prop, pdata->co_phase); if (aml_card_type_mmc(pdata)) { /*tx_phase set default value first*/ SD_PARSE_U32_PROP_DEC(child, "tx_phase", diff --git a/drivers/amlogic/mmc/emmc_partitions.c b/drivers/amlogic/mmc/emmc_partitions.c index f7ec036d90ed..ea01e71ee886 100644 --- a/drivers/amlogic/mmc/emmc_partitions.c +++ b/drivers/amlogic/mmc/emmc_partitions.c @@ -778,6 +778,41 @@ static int mmc_transfer(struct mmc_card *card, unsigned int dev_addr, return ret; } +/*write tuning para on emmc, the offset is 0x14400*/ +static int amlmmc_write_tuning_para(struct mmc_card *card, + unsigned int dev_addr) +{ + unsigned int size; + struct amlsd_platform *pdata = mmc_priv(card->host); + struct aml_tuning_para *parameter = &pdata->para; + unsigned int *buf; + int para_size; + int blocks; + + if (pdata->save_para == 0) + return 0; + + if (parameter->update == 0) + return 0; + parameter->update = 0; + + para_size = sizeof(struct aml_tuning_para); + blocks = (para_size - 1) / 512 + 1; + size = blocks << card->csd.read_blkbits; + + buf = kmalloc(size, GFP_KERNEL); + memset(buf, 0, size); + + memcpy(buf, parameter, sizeof(struct aml_tuning_para)); + + mmc_claim_host(card->host); + mmc_transfer(card, dev_addr, blocks, buf, 1); + mmc_release_host(card->host); + + kfree(buf); + return 0; +} + int mmc_read_internal(struct mmc_card *card, unsigned int dev_addr, unsigned int blocks, void *buf) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fbac7f275d89..08bcb7ec5c7f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1164,6 +1164,9 @@ static int mmc_select_hs400(struct mmc_card *card) err = mmc_switch_status(card); if (err) goto out_err; +#ifdef CONFIG_AMLOGIC_MMC + aml_read_tuning_para(host); +#endif /* Switch card to DDR */ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d1b40670f0c7..65915b10e798 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -720,6 +720,31 @@ static void thermal_zone_device_check(struct work_struct *work) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +int thermal_get_temp_by_index(int id) +{ + struct thermal_zone_device *pos = NULL; + int temperature, ret = 0; + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (pos->id == id) { + ret = 1; + break; + } + } + + if (ret) { + ret = thermal_zone_get_temp(pos, &temperature); + + if (ret) + return -1; + return temperature; + } + + return -1; +} +#endif + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 749d41abfbab..e132b61cee24 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -57,6 +57,9 @@ struct thermal_instance { int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); +#ifdef CONFIG_AMLOGIC_TEMP_SENSOR +int thermal_get_temp_by_index(int id); +#endif #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); void thermal_gov_step_wise_unregister(void); diff --git a/include/linux/amlogic/sd.h b/include/linux/amlogic/sd.h index 960d5ffcb610..5b0a2b8f5341 100644 --- a/include/linux/amlogic/sd.h +++ b/include/linux/amlogic/sd.h @@ -257,10 +257,40 @@ struct clock_lay_t { #define TODLY_MIN_NS (2) #define TODLY_MAX_NS (14) +struct hs400_para { + unsigned int delay1; + unsigned int delay2; + unsigned int intf3; + unsigned int flag; +}; + +struct hs200_para { + unsigned int adjust; +}; + +struct hs_para { + unsigned int adjust; +}; + +struct aml_tuning_para { + unsigned int chip_id[4]; + unsigned int magic; + unsigned int vddee; + struct hs400_para hs4[7]; + struct hs200_para hs2; + struct hs_para hs; + unsigned int version; + unsigned int busmod; + unsigned int update; + int temperature; + long long checksum; +}; + struct amlsd_platform { struct amlsd_host *host; struct mmc_host *mmc; struct list_head sibling; + struct aml_tuning_para para; u32 ocr_avail; u32 port; #define PORT_SDIO_A 0 @@ -281,6 +311,7 @@ struct amlsd_platform { unsigned int card_capacity; unsigned int tx_phase; unsigned int tx_delay; + unsigned int save_para; unsigned int co_phase; unsigned int f_min; unsigned int f_max; diff --git a/include/linux/mmc/emmc_partitions.h b/include/linux/mmc/emmc_partitions.h index e52ac0005fb2..6cd4e6ac0b24 100644 --- a/include/linux/mmc/emmc_partitions.h +++ b/include/linux/mmc/emmc_partitions.h @@ -23,6 +23,7 @@ /* MMC Partition Table */ #define MMC_PARTITIONS_MAGIC "MPT" #define MMC_RESERVED_NAME "reserved" +#define MMC_TUNING_NAME "tuning" #define SZ_1M 0x00100000 @@ -36,6 +37,7 @@ #define MMC_DTB_OFFSET ((SZ_1M*(36+4))/512) /* the size of bootloader partition */ #define MMC_BOOT_PARTITION_SIZE (4*SZ_1M) +#define MMC_TUNING_OFFSET 0X14400 /* the size of reserve space behind bootloader partition */ #define MMC_BOOT_PARTITION_RESERVED (32*SZ_1M) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 8ed2dd8f8ac0..71ba48cd0045 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -584,4 +584,6 @@ static inline void mmc_retune_recheck(struct mmc_host *host) void mmc_retune_pause(struct mmc_host *host); void mmc_retune_unpause(struct mmc_host *host); +int aml_read_tuning_para(struct mmc_host *mmc); + #endif /* LINUX_MMC_HOST_H */