#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"
*/
#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 {
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;
+
+ regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
+ delaynum = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
- return delaynum;
+ 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));
}
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;
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);
+ }
+
+ /* 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);
+
+ 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;
+
+ 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++){
+
+ 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));
- //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;
-
- 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);
- //smpl = smpl >> 1;//smpl = dw_mci_rockchip_move_next_clksmpl(host);
-
- mmc_wait_for_req(mmc, &mrq);
-
- if (!cmd.error && !data.error) {
- if (!memcmp(blk_pattern, blk_test, blksz)) {
- pos = smpl/8;
- index = smpl%8;
- candiates[pos] |= (1 << index);
-
- //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);
-
- 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;
-
- kfree(blk_test);
- return ret;
+ 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)){
+
+ 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_DBG_INFO_FUNC(host->mmc,
+ "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 AND SDMMC_SHIFT_DEGREE_180[%s]",
+ mmc_hostname(host->mmc));
+
+ /* 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)){
+
+ 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)){
+
+ 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)){
+
+ 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 */
#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 | \
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);
tempck, div);
host->set_speed = tempck;
- host->set_div = div;
+ host->set_div = div;
}
/* disable clock */
mci_writel(host, CLKSRC, 0);
/* inform CIU */
- mci_send_cmd(slot,
- SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
-
- 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();
+ 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);
+
+ 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);
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))
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;
}
}
- 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);