mmc: host: rk_sdmmc:

[IMPORTANT COMMIT]
        (1) Complete HS200 tuning algorithm for better performance & stability
        (2) Auto workaround for error emmc clk settingin dts by Linux BSP engineer.
        (3) Fixme: BUG on HS-DDR50 eMMC with div = 0
This commit is contained in:
lintao
2014-06-05 13:28:15 +08:00
parent 1a7dec640d
commit 0927e5aff1
2 changed files with 423 additions and 130 deletions

View File

@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/rockchip/cpu.h>
#include "rk_sdmmc.h"
#include "dw_mmc-pltfm.h"
@@ -32,16 +33,35 @@
*/
#define CRU_SDMMC_CON(id, tuning_type) (0x200 + ((id) * 8) + ((tuning_type) * 4))
#define MAX_DELAY_LINE (0xff)
#define FREQ_REF_150MHZ (150000000)
#define PRECISE_ADJUST (0)
#define SDMMC_TUNING_SEL(tuning_type) ( tuning_type? 10:11 )
#define SDMMC_TUNING_DELAYNUM(tuning_type) ( tuning_type? 2:3 )
#define SDMMC_TUNING_DEGREE(tuning_type) ( tuning_type? 0:1 )
#define SDMMC_TUNING_INIT_STATE (0)
#define SDMMC_SHIFT_DEGREE_0 (0)
#define SDMMC_SHIFT_DEGREE_90 (1)
#define SDMMC_SHIFT_DEGREE_180 (2)
#define SDMMC_SHIFT_DEGREE_270 (3)
enum{
SDMMC_SHIFT_DEGREE_0 = 0,
SDMMC_SHIFT_DEGREE_90,
SDMMC_SHIFT_DEGREE_180,
SDMMC_SHIFT_DEGREE_270,
SDMMC_SHIFT_DEGREE_INVALID,
};
const char *phase_desc[SDMMC_SHIFT_DEGREE_INVALID + 1] = {
"SDMMC_SHIFT_DEGREE_0",
"SDMMC_SHIFT_DEGREE_90",
"SDMMC_SHIFT_DEGREE_180",
"SDMMC_SHIFT_DEGREE_270",
"SDMMC_SHIFT_DEGREE_INVALID",
};
enum{
USE_CLK_AFTER_PHASE = 0,
USE_CLK_AFTER_PHASE_AND_DELAY_LINE = 1,
};
/* Variations in Rockchip specific dw-mshc controller */
enum dw_mci_rockchip_type {
@@ -115,39 +135,60 @@ static void dw_mci_rockchip_set_ios(struct dw_mci *host, struct mmc_ios *ios)
static int dw_mci_rockchip_parse_dt(struct dw_mci *host)
{
return 0;
return 0;
}
static inline u8 dw_mci_rockchip_get_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type)
{
u32 regs;
u8 delaynum;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
delaynum = (regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff;
u32 regs;
u8 delaynum;
return delaynum;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
delaynum = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
return delaynum;
}
static inline void dw_mci_rockchip_set_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 delaynum)
{
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
regs &= ~( 0xff << SDMMC_TUNING_DELAYNUM(tuning_type));
regs |= (delaynum << SDMMC_TUNING_DELAYNUM(tuning_type));
regs |= (0xff << (SDMMC_TUNING_DELAYNUM(tuning_type)+16));
MMC_DBG_INFO_FUNC(host->mmc,"tuning_result: con_id=%d, tuning_type=%d,SDMMC_CON=0x%x. [%s]",
con_id,tuning_type,regs, mmc_hostname(host->mmc));
cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
regs &= ~( 0xff << SDMMC_TUNING_DELAYNUM(tuning_type));
regs |= (delaynum << SDMMC_TUNING_DELAYNUM(tuning_type));
regs |= (0xff << (SDMMC_TUNING_DELAYNUM(tuning_type)+16));
MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[delayline]: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
con_id, tuning_type, regs, mmc_hostname(host->mmc));
cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
}
static inline void dw_mci_rockchip_set_degree(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 phase)
{
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
regs &= ~( 0x3 << SDMMC_TUNING_DEGREE(tuning_type));
regs |= (phase << SDMMC_TUNING_DEGREE(tuning_type));
regs |= (0x3 << (SDMMC_TUNING_DEGREE(tuning_type)+16));
cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[phase]: con_id = %d, tuning_type= %d, CRU_CON = 0x%x. [%s]",
con_id, tuning_type, regs, mmc_hostname(host->mmc));
cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
}
static inline void dw_mci_rockchip_turning_sel(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 mode)
{
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
regs &= ~( 0x1 << SDMMC_TUNING_SEL(tuning_type));
regs |= (mode << SDMMC_TUNING_SEL(tuning_type));
regs |= (0x1 << (SDMMC_TUNING_SEL(tuning_type)+16));
MMC_DBG_INFO_FUNC(host->mmc,"tuning_sel: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
con_id, tuning_type, regs, mmc_hostname(host->mmc));
cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
}
@@ -158,12 +199,12 @@ static inline u8 dw_mci_rockchip_get_phase(struct dw_mci *host, u8 con_id, u8 tu
static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 val)
{
u32 regs;
//u8 delaynum;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
u32 regs;
regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
if(tuning_type) {
val = (regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff;
val = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
}
return val;
@@ -172,106 +213,317 @@ static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_i
static u8 dw_mci_rockchip_get_best_clksmpl(u8 *candiates)
{
u8 pos, i;
u8 bestval =0;
u8 pos, i;
u8 bestval =0;
for(pos=31; pos>0;pos--)
if(candiates[pos] != 0) {
for(i=7; i>0; i--)
{
if(candiates[pos]& (1<<i))
bestval = pos*8+i;
}
for(pos = 31; pos > 0; pos--){
if(candiates[pos] != 0) {
for(i = 7; i > 0; i--){
if(candiates[pos]& (1<<i))
bestval = pos*8+i;
}
}
}
return bestval;
return bestval;
}
static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
u8 *blk_test, unsigned int blksz)
{
struct dw_mci *host = slot->host;
struct mmc_host *mmc = slot->mmc;
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_command stop = {0};
struct mmc_data data = {0};
struct scatterlist sg;
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
data.blksz = blksz;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, blk_test, blksz);
mrq.cmd = &cmd;
mrq.stop = &stop;
mrq.data = &data;
host->mrq = &mrq;
mci_writel(host, TMOUT, ~0);
mmc_wait_for_req(mmc, &mrq);
if(!cmd.error && !data.error){
return 0;
}else{
dev_dbg(host->dev,
"Tuning error: cmd.error:%d, data.error:%d\n",cmd.error, data.error);
return -EIO;
}
}
static int dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
struct dw_mci_tuning_data *tuning_data)
{
struct dw_mci *host = slot->host;
struct mmc_host *mmc = slot->mmc;
const u8 *blk_pattern = tuning_data->blk_pattern;
u8 *blk_test;
unsigned int blksz = tuning_data->blksz;
u8 start_smpl, smpl, pos,index;
u8 candiates[32];
u8 found = 0;
int ret = 0;
u8 step;
u8 candidates_delayline[MAX_DELAY_LINE] = {0};
u8 candidates_degree[SDMMC_SHIFT_DEGREE_INVALID] = {4,4,4,4};
u8 index = 0;
u8 start_degree = 0;
u32 start_delayline = 0;
u8 *blk_pattern = tuning_data->blk_pattern;
u8 *blk_test;
int ret = -1;
int ref = 0;
unsigned int blksz = tuning_data->blksz;
MMC_DBG_INFO_FUNC(host->mmc,"execute tuning: [%s]", mmc_hostname(host->mmc));
blk_test = kmalloc(blksz, GFP_KERNEL);
if (!blk_test)
{
MMC_DBG_ERR_FUNC(host->mmc,"execute tuning: blk_test kmalloc failed[%s]",
mmc_hostname(host->mmc));
return -ENOMEM;
//be fixed to 90 degrees
//dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
//start_smpl = dw_mci_rockchip_get_delaynum(host, tuning_data->con_id, tuning_data->tuning_type);
start_smpl = 0;
smpl = 0xff;
for(pos=0; pos<32; pos++)
candiates[pos] = 0;
}
do {
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_command stop = {0};
struct mmc_data data = {0};
struct scatterlist sg;
/* Select use delay line*/
dw_mci_rockchip_turning_sel(host, tuning_data->con_id, tuning_data->tuning_type,
USE_CLK_AFTER_PHASE_AND_DELAY_LINE);
/* For RK32XX signoff 150M clk, 1 cycle = 6.66ns , and 1/4 phase = 1.66ns.
Netlist level sample LT: 10.531ns / 42.126ps WC: 19.695ns / 76.936ps.
So we take average --- 60ps, (1.66ns/ 2) = 0.83(middle-value),TAKE 0.9
0.9 / 60ps = 15 delayline
*/
if(cpu_is_rk3288()){
ref = ((FREQ_REF_150MHZ + host->bus_hz - 1) / host->bus_hz);
step = (15 * ref);
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
if(step > MAX_DELAY_LINE){
step = MAX_DELAY_LINE;
MMC_DBG_WARN_FUNC(host->mmc,
"execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
}
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: SOC is RK3288, ref = %d, step = %d[%s]",
ref, step, mmc_hostname(host->mmc));
}else{
step = (15 * ((FREQ_REF_150MHZ / host->bus_hz) * 100)) / 100;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
if(step > MAX_DELAY_LINE){
step = MAX_DELAY_LINE;
MMC_DBG_WARN_FUNC(host->mmc,
"execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
}
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: SOC is UNKNOWN, step = %d[%s]",
step, mmc_hostname(host->mmc));
}
/* Loop degree from 0 ~ 270 */
for(start_degree = SDMMC_SHIFT_DEGREE_0; start_degree < SDMMC_SHIFT_DEGREE_270; start_degree++){
data.blksz = blksz;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, start_degree);
if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
if(!memcmp(blk_pattern, blk_test, blksz)){
/* Successfully tuning in this condition*/
candidates_degree[index] = start_degree;
index++;
}
}
}
MMC_DBG_INFO_FUNC(host->mmc,"\n execute tuning: candidates_degree = %s \t%s \t%s \t%s[%s]",
phase_desc[candidates_degree[0]], phase_desc[candidates_degree[1]],
phase_desc[candidates_degree[2]], phase_desc[candidates_degree[3]],
mmc_hostname(host->mmc));
sg_init_one(&sg, blk_test, blksz);
mrq.cmd = &cmd;
mrq.stop = &stop;
mrq.data = &data;
host->mrq = &mrq;
if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90)
&& (candidates_degree[2] == SDMMC_SHIFT_DEGREE_180)){
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 [%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id,
tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
ret = 0;
goto done;
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180)
&& (candidates_degree[2] == SDMMC_SHIFT_DEGREE_270)){
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 [%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id,
tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
ret = 0;
goto done;
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90)
&& (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
mci_writel(host, TMOUT, ~0);
//smpl = smpl >> 1;//smpl = dw_mci_rockchip_move_next_clksmpl(host);
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 ~ SDMMC_SHIFT_DEGREE_90[%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else if((candidates_degree[0]==SDMMC_SHIFT_DEGREE_0)
&& (candidates_degree[1]==SDMMC_SHIFT_DEGREE_180)){
mmc_wait_for_req(mmc, &mrq);
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 AND SDMMC_SHIFT_DEGREE_180[%s]",
mmc_hostname(host->mmc));
if (!cmd.error && !data.error) {
if (!memcmp(blk_pattern, blk_test, blksz)) {
pos = smpl/8;
index = smpl%8;
candiates[pos] |= (1 << index);
/* FixMe: NO sense any signal indicator make this case happen*/
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
goto delayline;
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180)
&& (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
//temporary settings,!!!!!!!!!!!!!!!
if(smpl<64)
break;
}
} else {
dev_dbg(host->dev,
"Tuning error: cmd.error:%d, data.error:%d\n",
cmd.error, data.error);
}
smpl = smpl >> 1;
} while (start_smpl != smpl);
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 ~ SDMMC_SHIFT_DEGREE_180[%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_270)){
found = dw_mci_rockchip_get_best_clksmpl((u8 *)&candiates[0]);
if (found >= 0)
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, found);//dw_mci_rockchip_set_clksmpl(host, found);
else
ret = -EIO;
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 ~ SDMMC_SHIFT_DEGREE_270[%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
kfree(blk_test);
return ret;
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_90 + n ~ SDMMC_SHIFT_DEGREE_180][%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
&& (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_0 + n ~ SDMMC_SHIFT_DEGREE_90][%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_270)){
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_270 [%s]",
mmc_hostname(host->mmc));
/*FixME: so urgly signal indicator, HW engineer help!*/
dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
#if PRECISE_ADJUST
goto delayline;
#else
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
ret = 0;
goto done;
#endif
}else{
MMC_DBG_ERR_FUNC(host->mmc,
"execute tuning: candidates_degree beyong limited case! [%s]",
mmc_hostname(host->mmc));
BUG();
}
delayline:
index = 0;
for(start_delayline = 0; start_delayline <= MAX_DELAY_LINE; start_delayline += step){
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id,
tuning_data->tuning_type, start_delayline);
if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
if(!memcmp(blk_pattern, blk_test, blksz)){
/* Successfully tuning in this condition*/
candidates_delayline[index] = start_delayline;
index++;
}
}
}
if((index < 2) && (index != 0)) {
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_delayline failed for only one element [%s]",
mmc_hostname(host->mmc));
/* Make step smaller, and re-calculate */
step = step >> 1;
index = 0;
goto delayline;
}else if(index >= 2){
/* Find it! */
MMC_DBG_INFO_FUNC(host->mmc,
"execute tuning: candidates_delayline calculate successfully [%s]",
mmc_hostname(host->mmc));
dw_mci_rockchip_set_delaynum(host, tuning_data->con_id,
tuning_data->tuning_type, candidates_delayline[index/2]);
ret = 0;
goto done;
}
done:
kfree(blk_test);
blk_test = NULL;
return ret;
}
/* Common capabilities of RK32XX SoC */

View File

@@ -45,8 +45,11 @@
#include "rk_sdmmc.h"
#include "rk_sdmmc_of.h"
#include <linux/regulator/rockchip_io_vol_domain.h>
#include "../../clk/rockchip/clk-ops.h"
#define RK_SDMMC_DRIVER_VERSION "Ver 1.00. The last modify date is 2014-05-05"
#define grf_writel(v, offset) do { writel_relaxed(v, RK_GRF_VIRT + offset); dsb(); } while (0)
#define RK_SDMMC_DRIVER_VERSION "Ver 1.10 2014-06-05"
/* Common flag combinations */
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
@@ -870,8 +873,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
u32 clk_en_a;
u32 sdio_int;
MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n",
__LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc));
MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n",
__LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc));
if (!clock) {
mci_writel(host, CLKENA, 0);
@@ -896,7 +899,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
tempck, div);
host->set_speed = tempck;
host->set_div = div;
host->set_div = div;
}
/* disable clock */
@@ -904,25 +907,63 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKSRC, 0);
/* inform CIU */
mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
if(clock == 400*1000){
MMC_DBG_BOOT_FUNC(host->mmc,
"dw_mci_setup_bus: argue clk_mmc workaround out 800K for init[%s]",
mmc_hostname(host->mmc));
/* RK3288 clk_mmc will change parents to 24MHz xtal*/
clk_set_rate(host->clk_mmc,800*1000);
if(div > 1){
if((host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)
&& host->bus_hz > 100000000){
printk("rk_sdmmc: emmc : div larger than 1, illegal clk in dts ![%s]\n ",
mmc_hostname(host->mmc));
printk("eMMC ERROR, emergancy halt!!!!!!!!!\n");
printk("Please refer to your eMMC datasheet to determine speed mode!\n");
printk("================================rk3288====================================");
printk("DDR 8-bits mode: clk in dts should be 100MHz!\n");
printk("DDR 4-bits mode: clk in dts should be <=100MHz(recommand 50 or 100Mhz)!\n");
printk("SDR mode: clk in dts should <= 100MHz(recommand 50 or 100Mhz)!\n");
printk("HS200 mode: clk in dts should <= 150MHz!\n");
printk("==========================================================================");
BUG();
div = 0;
host->set_div = div;
}
else
{
MMC_DBG_BOOT_FUNC(host->mmc,
"dw_mci_setup_bus: argue clk_mmc workaround out normal clock [%s]",
mmc_hostname(host->mmc));
if(div > 1)
{
MMC_DBG_ERR_FUNC(host->mmc,
"dw_mci_setup_bus: div SHOULD NOT LARGER THAN ONE! [%s]",
mmc_hostname(host->mmc));
div = 1;
host->set_div = div;
host->bus_hz = host->set_speed * 2;
MMC_DBG_BOOT_FUNC(host->mmc,
"dw_mci_setup_bus: workaround div = %d, host->bus_hz = %d [%s]",
div, host->bus_hz, mmc_hostname(host->mmc));
}
}
/* BUG may be here, come on, Linux BSP engineer looks!
FIXME: HS-DDR eMMC, div SHOULD be ONE, but we here cannot fetch eMMC bus mode!!!!!!!!
WRONG dts set clk = 50M, and calc div be zero. Controller denied this setting!
some oops happened like that:
mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 50000000Hz, actual 50000000HZ div = 0)
rk_sdmmc: BOOT dw_mci_setup_bus: argue clk_mmc workaround out normal clock [mmc0]
rk_sdmmc: BOOT Bus speed=50000000Hz,Bus width=8bits.[mmc0]
mmc0: new high speed DDR MMC card at address 0001
mmcblk0: mmc0:0001 M8G1GC 7.28 GiB
....
mmcblk0: error -84 transferring data, sector 606208, nr 32, cmd response 0x900, card status 0xb00
mmcblk0: retrying using single block read
mmcblk0: error -110 sending status command, retrying
How to: If eMMC HW version < 4.51, or > 4.51 but no caps2-mmc-hs200 support in dts
Please set dts emmc clk to 100M or 150M, I will workaround it!
*/
if (host->verid < DW_MMC_240A)
clk_set_rate(host->clk_mmc,(host->bus_hz));
else
clk_set_rate(host->clk_mmc,(host->bus_hz) * 2);
}
/* set clock to desired speed */
mci_writel(host, CLKDIV, div);
@@ -935,7 +976,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if (host->verid < DW_MMC_240A)
sdio_int = SDMMC_INT_SDIO(slot->id);
else
else
sdio_int = SDMMC_INT_SDIO((slot->id) + 8);
if (!(mci_readl(host, INTMASK) & sdio_int))
@@ -2834,7 +2875,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->hold_reg_flag |= drv_data->hold_reg_flag[ctrl_id];
//set the compatibility of driver.
mmc->caps |= MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE;
mmc->caps |= MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE ;
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
@@ -3248,10 +3289,10 @@ int dw_mci_probe(struct dw_mci *host)
}
}
host->quirks = host->pdata->quirks;
host->irq_state = true;
host->set_speed = 0;
host->set_div = 0;
host->quirks = host->pdata->quirks;
host->irq_state = true;
host->set_speed = 0;
host->set_div = 0;
spin_lock_init(&host->lock);
INIT_LIST_HEAD(&host->queue);